|
21 | 21 | #ifdef USE_AES
|
22 | 22 | #include "mbedtls/aes.h"
|
23 | 23 | #endif
|
| 24 | +#ifdef USE_AES_CCM |
| 25 | +#include "mbedtls/ccm.h" |
| 26 | +#endif |
24 | 27 | #ifndef USE_SHA1_JS
|
25 | 28 | #include "mbedtls/sha1.h"
|
26 | 29 | #endif
|
@@ -92,6 +95,9 @@ const char *jswrap_crypto_error_to_str(int err) {
|
92 | 95 | case MBEDTLS_ERR_MD_BAD_INPUT_DATA: return "Bad input data";
|
93 | 96 | #ifdef USE_AES
|
94 | 97 | case MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH: return "Invalid input length";
|
| 98 | +#endif |
| 99 | +#ifdef USE_AES_CCM |
| 100 | + case MBEDTLS_ERR_CCM_AUTH_FAILED: return "Authenticated decryption failed"; |
95 | 101 | #endif
|
96 | 102 | }
|
97 | 103 | return 0;
|
@@ -485,3 +491,131 @@ JsVar *jswrap_crypto_AES_decrypt(JsVar *message, JsVar *key, JsVar *options) {
|
485 | 491 | return jswrap_crypto_AEScrypt(message, key, options, false);
|
486 | 492 | }
|
487 | 493 | #endif
|
| 494 | + |
| 495 | +#ifdef USE_AES_CCM |
| 496 | +// if encrypting, tagArg is tag length |
| 497 | +// if decrypting, tagArg is tag that came with the message |
| 498 | +JsVar *jswrap_crypto_AES_ccmCrypt(JsVar *message, JsVar *key, JsVar *iv, JsVar *tagArg, bool encrypt) { |
| 499 | + unsigned char ivVal[13]; |
| 500 | + memset(ivVal, 0, sizeof(ivVal)); |
| 501 | + int ivLenVal = 0; |
| 502 | + if (jsvIsArray(iv) || jsvIsArrayBuffer(iv)){ |
| 503 | + jsvIterateCallbackToBytes(iv, ivVal, sizeof(ivVal)); |
| 504 | + ivLenVal = jsvGetLength(iv); |
| 505 | + if (ivLenVal < 2 || ivLenVal > sizeof(ivVal)) { |
| 506 | + jswrap_crypto_error(MBEDTLS_ERR_MD_BAD_INPUT_DATA); |
| 507 | + return NULL; |
| 508 | + } |
| 509 | + } else { |
| 510 | + jswrap_crypto_error(MBEDTLS_ERR_MD_BAD_INPUT_DATA); |
| 511 | + return NULL; |
| 512 | + } |
| 513 | + |
| 514 | + JSV_GET_AS_CHAR_ARRAY(messagePtr, messageLen, message); |
| 515 | + if (!messagePtr) return NULL; |
| 516 | + |
| 517 | + JSV_GET_AS_CHAR_ARRAY(keyPtr, keyLen, key); |
| 518 | + if (!keyPtr) return NULL; |
| 519 | + |
| 520 | + char *outMessagePtr = NULL; |
| 521 | + JsVar *outVar = jsvNewArrayBufferWithPtr(messageLen, &outMessagePtr); |
| 522 | + if (!outMessagePtr) return NULL; |
| 523 | + |
| 524 | + int err = 0; |
| 525 | + mbedtls_ccm_context ctx; |
| 526 | + mbedtls_ccm_init(&ctx); |
| 527 | + err = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, (unsigned char*)keyPtr, (unsigned int)keyLen*8); |
| 528 | + if (err == 0) { |
| 529 | + if (encrypt) { |
| 530 | + // encrypt and generate tag |
| 531 | + int tagLenVal = 0; |
| 532 | + if (jsvIsNumeric(tagArg)) { |
| 533 | + tagLenVal = jsvGetInteger(tagArg); |
| 534 | + } else { |
| 535 | + err = MBEDTLS_ERR_MD_BAD_INPUT_DATA; |
| 536 | + } |
| 537 | + if (err == 0) { |
| 538 | + if (tagLenVal < 4 || tagLenVal > 16 || tagLenVal % 2 != 0) { |
| 539 | + // invalid tag length; must be one of 4, 6, 8, 10, 12, 14 or 16 |
| 540 | + err = MBEDTLS_ERR_MD_BAD_INPUT_DATA; |
| 541 | + } |
| 542 | + } |
| 543 | + if (err == 0) { |
| 544 | + unsigned char tag[tagLenVal]; |
| 545 | + memset(tag, 0, sizeof(tag)); |
| 546 | + err = mbedtls_ccm_encrypt_and_tag(&ctx, messageLen, ivVal, ivLenVal, NULL, 0, (unsigned char*)messagePtr, (unsigned char*)outMessagePtr, tag, sizeof(tag)); |
| 547 | + if (err == 0) { |
| 548 | + JsVar *outMessageVar = outVar; |
| 549 | + outVar = jsvNewObject(); |
| 550 | + jsvObjectSetChildAndUnLock(outVar, "data", outMessageVar); |
| 551 | + jsvObjectSetChildAndUnLock(outVar, "tag", jsvNewArrayBufferWithData(tagLenVal, tag)); |
| 552 | + } |
| 553 | + } |
| 554 | + } else { |
| 555 | + // decrypt and check tag |
| 556 | + JSV_GET_AS_CHAR_ARRAY(tagPtr, tagLen, tagArg); |
| 557 | + if (!tagPtr) { |
| 558 | + err = MBEDTLS_ERR_MD_BAD_INPUT_DATA; |
| 559 | + } |
| 560 | + if (err == 0) { |
| 561 | + err = mbedtls_ccm_auth_decrypt(&ctx, messageLen, ivVal, ivLenVal, NULL, 0, (unsigned char*)messagePtr, (unsigned char*)outMessagePtr, (unsigned char*)tagPtr, tagLen); |
| 562 | + } |
| 563 | + } |
| 564 | + } |
| 565 | + mbedtls_ccm_free(&ctx); |
| 566 | + |
| 567 | + if (err == 0) { |
| 568 | + return outVar; |
| 569 | + } else { |
| 570 | + jswrap_crypto_error(err); |
| 571 | + jsvUnLock(outVar); |
| 572 | + return NULL; |
| 573 | + } |
| 574 | +} |
| 575 | + |
| 576 | +/*TYPESCRIPT |
| 577 | +type AES_CCM_EncryptResult = { |
| 578 | + data: ArrayBuffer, |
| 579 | + tag: ArrayBuffer, |
| 580 | +}; |
| 581 | +*/ |
| 582 | + |
| 583 | +/*JSON{ |
| 584 | + "type" : "staticmethod", |
| 585 | + "class" : "AES", |
| 586 | + "name" : "ccmEncrypt", |
| 587 | + "generate" : "jswrap_crypto_AES_ccmEncrypt", |
| 588 | + "params" : [ |
| 589 | + ["message","JsVar","Message to encrypt"], |
| 590 | + ["key","JsVar","Key to encrypt message - must be an `ArrayBuffer` of 128, 192, or 256 BITS"], |
| 591 | + ["iv","JsVar","nonce (initialization vector) - must be an `ArrayBuffer` of 2 to 13 bytes"], |
| 592 | + ["tagLen","JsVar","Length of tag to generate in bytes - must be one of 4, 6, 8, 10, 12, 14 or 16"] |
| 593 | + ], |
| 594 | + "return" : ["JsVar","An object"], |
| 595 | + "return_object" : "AES_CCM_EncryptResult", |
| 596 | + "ifdef" : "USE_AES_CCM" |
| 597 | +} |
| 598 | +*/ |
| 599 | +JsVar *jswrap_crypto_AES_ccmEncrypt(JsVar *message, JsVar *key, JsVar *iv, JsVar *tagLen) { |
| 600 | + return jswrap_crypto_AES_ccmCrypt(message, key, iv, tagLen, true); |
| 601 | +} |
| 602 | + |
| 603 | +/*JSON{ |
| 604 | + "type" : "staticmethod", |
| 605 | + "class" : "AES", |
| 606 | + "name" : "ccmDecrypt", |
| 607 | + "generate" : "jswrap_crypto_AES_ccmDecrypt", |
| 608 | + "params" : [ |
| 609 | + ["message","JsVar","Message to decrypt"], |
| 610 | + ["key","JsVar","Key to decrypt message - must be an `ArrayBuffer` of 128, 192, or 256 BITS"], |
| 611 | + ["iv","JsVar","Nonce (initialization vector) - must be an `ArrayBuffer` of 2 to 13 bytes"], |
| 612 | + ["tag","JsVar","Tag that came with the message - must be an `ArrayBuffer`"] |
| 613 | + ], |
| 614 | + "return" : ["JsVar","Decrypted message, or null on error (for example if the tag doesn't match)"], |
| 615 | + "ifdef" : "USE_AES_CCM" |
| 616 | +} |
| 617 | +*/ |
| 618 | +JsVar *jswrap_crypto_AES_ccmDecrypt(JsVar *message, JsVar *key, JsVar *iv, JsVar *tag) { |
| 619 | + return jswrap_crypto_AES_ccmCrypt(message, key, iv, tag, false); |
| 620 | +} |
| 621 | +#endif |
0 commit comments