From d65e94a698255bea6326cf5c597005e667b59a1b Mon Sep 17 00:00:00 2001 From: Anthony Cagliano Date: Sat, 23 Sep 2023 00:08:32 -0400 Subject: [PATCH] finish aes --- cryptx.h | 16 - docs/doxygen/xml/cryptx_8h.xml | 648 +++++++++--------- docs/doxygen/xml/structCipher.xml | 2 +- docs/doxygen/xml/structcryptx__aes__ctx.xml | 14 +- docs/doxygen/xml/structcryptx__ecc__point.xml | 6 +- docs/modules/aes.rst | 57 +- docs/www/_sources/modules/aes.rst.txt | 57 +- docs/www/modules/aes.html | 128 ++-- docs/www/modules/hmac.html | 4 - docs/www/objects.inv | Bin 6138 -> 6136 bytes docs/www/searchindex.js | 2 +- 11 files changed, 497 insertions(+), 437 deletions(-) diff --git a/cryptx.h b/cryptx.h index eb80a778..f2662dc2 100644 --- a/cryptx.h +++ b/cryptx.h @@ -172,9 +172,6 @@ void cryptx_hmac_update(struct cryptx_hmac_ctx* context, const void* data, size_ * @brief Output digest for current HMAC-state (preserves state). * @param context Pointer to an HMAC-state context. * @param digest Pointer to a buffer to write digest to. - * @note @b digest must be at large enough to hold the hash digest. - * You can retrieve the necessary size by accessing the @b digest_len - * member of an initialized @b cryptx_hmac_ctx. */ void cryptx_hmac_digest(struct cryptx_hmac_ctx* context, void* digest); @@ -341,10 +338,6 @@ aes_error_t cryptx_aes_init(struct cryptx_aes_ctx* context, * @param len Length of data at @b plaintext to encrypt. * @param ciphertext Pointer to buffer to write encrypted data to. * @returns An @b aes_error_t indicating the status of the AES operation. - * @note @b ciphertext should large enough to hold the encrypted message. - * See the @b cryptx_aes_get_ciphertext_len macro for CBC mode. - * @note @b plaintext and @b ciphertext are aliasable. - * @note Encrypt is streamable, such that \f$ encrypt(msg1) + encrypt(msg2) \f$ is functionally identical to \f$ encrypt(msg1+msg2) \f$ with the exception of intervening padding in CBC mode. */ aes_error_t cryptx_aes_encrypt(const struct cryptx_aes_ctx* context, const void* plaintext, @@ -358,8 +351,6 @@ aes_error_t cryptx_aes_encrypt(const struct cryptx_aes_ctx* context, * @param len Length of data at @b ciphertext to decrypt. * @param plaintext Pointer to buffer to write decryped data to. * @returns An @b aes_error_t indicating the status of the AES operation. - * @note @b plaintext and @b ciphertext are aliasable. - * @note Decrypt is streamable, such that \f$ decrypt(msg1) + decrypt(msg2) \f$ is functionally identical to \f$ decrypt(msg1+msg2) \f$ with the exception of intervening padding in CBC mode. */ aes_error_t cryptx_aes_decrypt(const struct cryptx_aes_ctx* context, const void* ciphertext, @@ -373,8 +364,6 @@ aes_error_t cryptx_aes_decrypt(const struct cryptx_aes_ctx* context, * @param aad Pointer to additional authenticated data segment. * @param aad_len Length of additional data segment. * @returns An @b aes_error_t indicating the status of the AES operation. - * @note This function can only be called between cipher initialization and encryption/decryption. Attempting to - * call this function at any other time will return @b AES_INVALID_OPERATION. */ aes_error_t cryptx_aes_update_aad(struct cryptx_aes_ctx* context, const void* aad, size_t aad_len); @@ -384,10 +373,6 @@ aes_error_t cryptx_aes_update_aad(struct cryptx_aes_ctx* context, * @param context Pointer to an AES context * @param digest Pointer to a buffer to output digest to. Must be at least 16 bytes large. * @returns An @b aes_error_t indicating the status of the AES operation. - * @warning Nonce-misuse vulnerability/forbidden attack: GCM is vulnerable to key discovery - * if the same nonce is reused to encrypt or decrypt multiple messages. Once you generate a digest for data processed - * by the cipher, cycle the nonce by generating a new one and calling @b cryptx_aes_init again with the new nonce. - * To ensure this is done properly, the context is marked invalid once this function is called. */ aes_error_t cryptx_aes_digest(struct cryptx_aes_ctx* context, uint8_t *digest); @@ -401,7 +386,6 @@ aes_error_t cryptx_aes_digest(struct cryptx_aes_ctx* context, uint8_t *digest); * @param ciphertext_len Length of ciphertext to authenticate. * @param tag Pointer to expected auth tag to validate against. * @returns TRUE if authentication tag matches expected, FALSE otherwise. - * @note If this function returns FALSE, do not decrypt the message. */ bool cryptx_aes_verify(const struct cryptx_aes_ctx* context, diff --git a/docs/doxygen/xml/cryptx_8h.xml b/docs/doxygen/xml/cryptx_8h.xml index d11e94e7..a7b1f4a1 100644 --- a/docs/doxygen/xml/cryptx_8h.xml +++ b/docs/doxygen/xml/cryptx_8h.xml @@ -69,7 +69,7 @@ - + CRYPTX_KEYLEN_AES192 @@ -79,7 +79,7 @@ - + CRYPTX_KEYLEN_AES256 @@ -89,7 +89,7 @@ - + CRYPTX_BLOCKSIZE_AES @@ -99,7 +99,7 @@ - + cryptx_aes_cbc_flagset @@ -111,7 +111,7 @@ - + cryptx_aes_ctr_flagset @@ -124,7 +124,7 @@ - + cryptx_aes_gcm_flagset @@ -135,7 +135,7 @@ - + cryptx_aes_get_ciphertext_len @@ -147,7 +147,7 @@ - + CRYPTX_RSA_MODULUS_MAX @@ -158,7 +158,7 @@ - + CRYPTX_EC_PRIVKEY_LEN @@ -173,7 +173,7 @@ - + CRYPTX_EC_PUBKEY_LEN @@ -186,7 +186,7 @@ - + CRYPTX_EC_SECRET_LEN @@ -196,7 +196,7 @@ - + cryptx_asn1_get_tag @@ -208,7 +208,7 @@ - + cryptx_asn1_get_class @@ -220,7 +220,7 @@ - + cryptx_asn1_get_form @@ -232,7 +232,7 @@ - + cryptx_base64_get_encoded_len @@ -247,7 +247,7 @@ - + cryptx_base64_get_decoded_len @@ -259,7 +259,7 @@ - + CRYPTX_GF2_INTLEN @@ -270,7 +270,7 @@ - + @@ -334,7 +334,7 @@ - + @@ -371,7 +371,7 @@ - + @@ -407,7 +407,7 @@ - + @@ -475,7 +475,7 @@ - + @@ -530,7 +530,7 @@ - + @@ -570,7 +570,7 @@ - + @@ -801,7 +801,7 @@ - + @@ -845,7 +845,7 @@ - + @@ -872,7 +872,7 @@ - + @@ -918,7 +918,7 @@ - + @@ -1312,13 +1312,11 @@ -digest must be at large enough to hold the hash digest. You can retrieve the necessary size by accessing the digest_len member of an initialized cryptx_hmac_ctx. - - + void @@ -1430,7 +1428,7 @@ - + bool @@ -1482,7 +1480,7 @@ - + bool @@ -1503,7 +1501,7 @@ - + bool @@ -1555,7 +1553,7 @@ - + bool @@ -1595,7 +1593,7 @@ - + bool @@ -1649,7 +1647,7 @@ - + uint32_t @@ -1669,7 +1667,7 @@ - + bool @@ -1713,7 +1711,7 @@ - + aes_error_t @@ -1815,7 +1813,7 @@ - + aes_error_t @@ -1877,17 +1875,11 @@ An aes_error_t indicating the status of the AES operation. -ciphertext should large enough to hold the encrypted message. See the cryptx_aes_get_ciphertext_len macro for CBC mode. - -plaintext and ciphertext are aliasable. - -Encrypt is streamable, such that $ encrypt(msg1) + encrypt(msg2) $ is functionally identical to $ encrypt(msg1+msg2) $ with the exception of intervening padding in CBC mode. - - + aes_error_t @@ -1949,15 +1941,11 @@ An aes_error_t indicating the status of the AES operation. -plaintext and ciphertext are aliasable. - -Decrypt is streamable, such that $ decrypt(msg1) + decrypt(msg2) $ is functionally identical to $ decrypt(msg1+msg2) $ with the exception of intervening padding in CBC mode. - - + aes_error_t @@ -2007,13 +1995,11 @@ An aes_error_t indicating the status of the AES operation. -This function can only be called between cipher initialization and encryption/decryption. Attempting to call this function at any other time will return AES_INVALID_OPERATION. - - + aes_error_t @@ -2051,13 +2037,11 @@ An aes_error_t indicating the status of the AES operation. -Nonce-misuse vulnerability/forbidden attack: GCM is vulnerable to key discovery if the same nonce is reused to encrypt or decrypt multiple messages. Once you generate a digest for data processed by the cipher, cycle the nonce by generating a new one and calling cryptx_aes_init again with the new nonce. To ensure this is done properly, the context is marked invalid once this function is called. - - + bool @@ -2143,13 +2127,11 @@ TRUE if authentication tag matches expected, FALSE otherwise. -If this function returns FALSE, do not decrypt the message. - - + rsa_error_t @@ -2243,7 +2225,7 @@ - + ec_error_t @@ -2289,7 +2271,7 @@ - + ec_error_t @@ -2347,7 +2329,7 @@ - + asn1_error_t @@ -2441,7 +2423,7 @@ - + size_t @@ -2497,7 +2479,7 @@ - + size_t @@ -2553,7 +2535,7 @@ - + void @@ -2607,7 +2589,7 @@ - + void @@ -2661,7 +2643,7 @@ - + bool @@ -2753,7 +2735,7 @@ - + bool @@ -2833,7 +2815,7 @@ - + void @@ -2899,7 +2881,7 @@ - + struct cryptx_ecc_point @@ -2941,7 +2923,7 @@ - + void @@ -2971,7 +2953,7 @@ - + void @@ -3025,7 +3007,7 @@ - + @@ -3134,267 +3116,267 @@ voidcryptx_hmac_update(structcryptx_hmac_ctx*context,constvoid*data,size_tlen); -voidcryptx_hmac_digest(structcryptx_hmac_ctx*context,void*digest); - -voidcryptx_hmac_pbkdf2(constchar*password, -size_tpasslen, -constvoid*salt, -size_tsaltlen, -uint8_t*key, -size_tkeylen, -size_trounds, -uint8_thash_alg); - -boolcryptx_bytes_tostring(constvoid*buf,size_tlen,char*hexstr); - +voidcryptx_hmac_digest(structcryptx_hmac_ctx*context,void*digest); + +voidcryptx_hmac_pbkdf2(constchar*password, +size_tpasslen, +constvoid*salt, +size_tsaltlen, +uint8_t*key, +size_tkeylen, +size_trounds, +uint8_thash_alg); + +boolcryptx_bytes_tostring(constvoid*buf,size_tlen,char*hexstr); + + + +boolcryptx_bytes_fromstring(void*buf,constchar*hexstr); - -boolcryptx_bytes_fromstring(void*buf,constchar*hexstr); - -boolcryptx_bytes_rcopy(void*dest,constvoid*src,size_tlen); - -boolcryptx_bytes_reverse(void*buf,size_tlen); - -boolcryptx_bytes_compare(constvoid*buf1,constvoid*buf2,size_tlen); - - -uint32_tcryptx_csrand_get(void); - -boolcryptx_csrand_fill(void*buffer,size_tsize); - -structcryptx_aes_ctx{ -uint24_tkeysize; -uint32_tround_keys[60]; -uint8_tiv[16]; -uint8_tciphermode; -uint8_top_assoc; -cryptx_aes_private_hmetadata; -}; - -enumcryptx_aes_cipher_modes{ -CRYPTX_AES_CBC, -CRYPTX_AES_CTR, -CRYPTX_AES_GCM -}; - -enumcryptx_aes_padding_schemes{ -PAD_PKCS7, -PAD_DEFAULT=PAD_PKCS7, -PAD_ISO2 -}; - -#defineCRYPTX_KEYLEN_AES12816 -#defineCRYPTX_KEYLEN_AES19224 -#defineCRYPTX_KEYLEN_AES25632 -#defineCRYPTX_BLOCKSIZE_AES16 -enumcryptx_aes_default_flags{ -CRYPTX_AES_CBC_DEFAULTS=(PAD_DEFAULT|0), -CRYPTX_AES_CTR_DEFAULTS=(((0x0f&(8))<<6)|((0x0f&(8))<<2)|0), -CRYPTX_AES_GCM_DEFAULTS=(0) -}; - -#definecryptx_aes_cbc_flagset(padding_mode)\ -(padding_mode)|0 - -#definecryptx_aes_ctr_flagset(nonce_len,counter_len)\ -((0x0f&(counter_len))<<6)|((0x0f&(nonce_len))<<2)|0 +boolcryptx_bytes_rcopy(void*dest,constvoid*src,size_tlen); + +boolcryptx_bytes_reverse(void*buf,size_tlen); + +boolcryptx_bytes_compare(constvoid*buf1,constvoid*buf2,size_tlen); + + +uint32_tcryptx_csrand_get(void); + +boolcryptx_csrand_fill(void*buffer,size_tsize); + +structcryptx_aes_ctx{ +uint24_tkeysize; +uint32_tround_keys[60]; +uint8_tiv[16]; +uint8_tciphermode; +uint8_top_assoc; +cryptx_aes_private_hmetadata; +}; + +enumcryptx_aes_cipher_modes{ +CRYPTX_AES_CBC, +CRYPTX_AES_CTR, +CRYPTX_AES_GCM +}; + +enumcryptx_aes_padding_schemes{ +PAD_PKCS7, +PAD_DEFAULT=PAD_PKCS7, +PAD_ISO2 +}; + +#defineCRYPTX_KEYLEN_AES12816 +#defineCRYPTX_KEYLEN_AES19224 +#defineCRYPTX_KEYLEN_AES25632 +#defineCRYPTX_BLOCKSIZE_AES16 +enumcryptx_aes_default_flags{ +CRYPTX_AES_CBC_DEFAULTS=(PAD_DEFAULT|0), +CRYPTX_AES_CTR_DEFAULTS=(((0x0f&(8))<<6)|((0x0f&(8))<<2)|0), +CRYPTX_AES_GCM_DEFAULTS=(0) +}; + +#definecryptx_aes_cbc_flagset(padding_mode)\ +(padding_mode)|0 + +#definecryptx_aes_ctr_flagset(nonce_len,counter_len)\ +((0x0f&(counter_len))<<6)|((0x0f&(nonce_len))<<2)|0 + +#definecryptx_aes_gcm_flagset0 -#definecryptx_aes_gcm_flagset0 - -#definecryptx_aes_get_ciphertext_len(len)\ -((((len)%CRYPTX_BLOCKSIZE_AES)==0)?(len)+CRYPTX_BLOCKSIZE_AES:(((len)>>4)+1)<<4) - -typedefenum{ -AES_OK, -AES_INVALID_ARG, -AES_INVALID_MSG, -AES_INVALID_CIPHERMODE, -AES_INVALID_PADDINGMODE, -AES_INVALID_CIPHERTEXT, -AES_INVALID_OPERATION -}aes_error_t; - -aes_error_tcryptx_aes_init(structcryptx_aes_ctx*context, -constvoid*key, -size_tkeylen, -constvoid*iv, -size_tivlen, -uint8_tcipher_mode, -uint24_tflags); - -aes_error_tcryptx_aes_encrypt(conststructcryptx_aes_ctx*context, -constvoid*plaintext, -size_tlen, -void*ciphertext); - -aes_error_tcryptx_aes_decrypt(conststructcryptx_aes_ctx*context, -constvoid*ciphertext, -size_tlen, -void*plaintext); - -aes_error_tcryptx_aes_update_aad(structcryptx_aes_ctx*context, -constvoid*aad,size_taad_len); - -aes_error_tcryptx_aes_digest(structcryptx_aes_ctx*context,uint8_t*digest); - -boolcryptx_aes_verify(conststructcryptx_aes_ctx*context, -constvoid*aad,size_taad_len, -constvoid*ciphertext,size_tciphertext_len, -uint8_t*tag); - - -typedefenum{ -RSA_OK, -RSA_INVALID_ARG, -RSA_INVALID_MSG, -RSA_INVALID_MODULUS, -RSA_ENCODING_ERROR -}rsa_error_t; - -#defineCRYPTX_RSA_MODULUS_MAX256 - -rsa_error_tcryptx_rsa_encrypt(constvoid*msg, -size_tmsglen, -constvoid*pubkey, -size_tkeylen, -void*ciphertext, -uint8_toaep_hash_alg); - - - -#defineCRYPTX_EC_PRIVKEY_LEN30 - -#defineCRYPTX_EC_PUBKEY_LEN(CRYPTX_ECDH_PRIVKEY_LEN<<1) -#defineCRYPTX_EC_SECRET_LENCRYPTX_ECDH_PUBKEY_LEN - -typedefenum_ec_error{ -EC_OK, -EC_INVALID_ARG, -EC_PRIVKEY_INVALID, -EC_RPUBKEY_INVALID -}ec_error_t; - -ec_error_tcryptx_ec_keygen(uint8_t*privkey,uint8_t*pubkey); +#definecryptx_aes_get_ciphertext_len(len)\ +((((len)%CRYPTX_BLOCKSIZE_AES)==0)?(len)+CRYPTX_BLOCKSIZE_AES:(((len)>>4)+1)<<4) + +typedefenum{ +AES_OK, +AES_INVALID_ARG, +AES_INVALID_MSG, +AES_INVALID_CIPHERMODE, +AES_INVALID_PADDINGMODE, +AES_INVALID_CIPHERTEXT, +AES_INVALID_OPERATION +}aes_error_t; + +aes_error_tcryptx_aes_init(structcryptx_aes_ctx*context, +constvoid*key, +size_tkeylen, +constvoid*iv, +size_tivlen, +uint8_tcipher_mode, +uint24_tflags); + +aes_error_tcryptx_aes_encrypt(conststructcryptx_aes_ctx*context, +constvoid*plaintext, +size_tlen, +void*ciphertext); + +aes_error_tcryptx_aes_decrypt(conststructcryptx_aes_ctx*context, +constvoid*ciphertext, +size_tlen, +void*plaintext); + +aes_error_tcryptx_aes_update_aad(structcryptx_aes_ctx*context, +constvoid*aad,size_taad_len); + +aes_error_tcryptx_aes_digest(structcryptx_aes_ctx*context,uint8_t*digest); + +boolcryptx_aes_verify(conststructcryptx_aes_ctx*context, +constvoid*aad,size_taad_len, +constvoid*ciphertext,size_tciphertext_len, +uint8_t*tag); + + +typedefenum{ +RSA_OK, +RSA_INVALID_ARG, +RSA_INVALID_MSG, +RSA_INVALID_MODULUS, +RSA_ENCODING_ERROR +}rsa_error_t; + +#defineCRYPTX_RSA_MODULUS_MAX256 + +rsa_error_tcryptx_rsa_encrypt(constvoid*msg, +size_tmsglen, +constvoid*pubkey, +size_tkeylen, +void*ciphertext, +uint8_toaep_hash_alg); + + + +#defineCRYPTX_EC_PRIVKEY_LEN30 + +#defineCRYPTX_EC_PUBKEY_LEN(CRYPTX_ECDH_PRIVKEY_LEN<<1) +#defineCRYPTX_EC_SECRET_LENCRYPTX_ECDH_PUBKEY_LEN + +typedefenum_ec_error{ +EC_OK, +EC_INVALID_ARG, +EC_PRIVKEY_INVALID, +EC_RPUBKEY_INVALID +}ec_error_t; + +ec_error_tcryptx_ec_keygen(uint8_t*privkey,uint8_t*pubkey); + +ec_error_tcryptx_ec_secret(constuint8_t*privkey,constuint8_t*rpubkey,uint8_t*secret); -ec_error_tcryptx_ec_secret(constuint8_t*privkey,constuint8_t*rpubkey,uint8_t*secret); - - -enumCRYPTX_ASN1_TAGS{ -ASN1_RESVD=0, -ASN1_BOOLEAN, -ASN1_INTEGER, -ASN1_BITSTRING, -ASN1_OCTETSTRING, -ASN1_NULL, -ASN1_OBJECTID, -ASN1_OBJECTDESC, -ASN1_INSTANCE, -ASN1_REAL, -ASN1_ENUMERATED, -ASN1_EMBEDDEDPDV, -ASN1_UTF8STRING, -ASN1_RELATIVEOID, -ASN1_SEQUENCE=16, -ASN1_SET, -ASN1_NUMERICSTRING, -ASN1_PRINTABLESTRING, -ASN1_TELETEXSTRING, -ASN1_VIDEOTEXSTRING, -ASN1_IA5STRING, -ASN1_UTCTIME, -ASN1_GENERALIZEDTIME, -ASN1_GRAPHICSTRING, -ASN1_VISIBLESTRING, -ASN1_GENERALSTRING, -ASN1_UNIVERSALSTRING, -ASN1_CHARSTRING, -ASN1_BMPSTRING -}; - -enumCRYPTX_ASN1_CLASSES{ -ASN1_UNIVERSAL, -ASN1_APPLICATION, -ASN1_CONTEXTSPEC, -ASN1_PRIVATE -}; - -enumCRYPTX_ASN1_FORMS{ -ASN1_PRIMITIVE, -ASN1_CONSTRUCTED, -}; - -#definecryptx_asn1_get_tag(tag)((tag)&0b111111) -#definecryptx_asn1_get_class(tag)(((tag)>>6)&0b11) -#definecryptx_asn1_get_form(tag)(((tag)>>5)&1) - -typedefenum{ -ASN1_OK, -ASN1_END_OF_FILE, -ASN1_INVALID_ARG, -ASN1_LEN_OVERFLOW, -}asn1_error_t; - -asn1_error_tcryptx_asn1_decode( -void*data_start, -size_tdata_len, -uint8_tseek_to, -uint8_t*element_tag, -size_t*element_len, -uint8_t**element_data); - - - -#definecryptx_base64_get_encoded_len(len)((len)*4/3) -#definecryptx_base64_get_decoded_len(len)((len)*3/4) - -size_tcryptx_base64_encode(void*dest,constvoid*src,size_tlen); - -size_tcryptx_base64_decode(void*dest,constvoid*src,size_tlen); - - -#ifdefCRYPTX_ENABLE_HAZMAT - -voidcryptx_hazmat_aes_ecb_encrypt(constvoid*block_in, -void*block_out, -structcryptx_aes_ctx*ks); - -voidcryptx_hazmat_aes_ecb_decrypt(constvoid*block_in, -void*block_out, -structcryptx_aes_ctx*ks); - -boolcryptx_hazmat_rsa_oaep_encode(constvoid*plaintext, -size_tlen, -void*encoded, -size_tmodulus_len, -constuint8_t*auth, -uint8_thash_alg); - -boolcryptx_hazmat_rsa_oaep_decode(constvoid*encoded, -size_tlen, -void*plaintext, -constuint8_t*auth, -uint8_thash_alg); - -voidcryptx_hazmat_powmod(uint8_tsize,uint8_t*restrictbase,uint24_texp,constuint8_t*restrictmod); - -#defineCRYPTX_GF2_INTLEN30 - -structcryptx_ecc_point{ -uint8_tx[CRYPTX_GF2_INTLEN]; -uint8_ty[CRYPTX_GF2_INTLEN]; -} - -voidcryptx_hazmat_ecc_point_add(cryptx_ecc_point*p,cryptx_ecc_point*q); - -voidcryptx_hazmat_ecc_point_double(cryptx_ecc_point*p); - -voidcryptx_hazmat_ecc_point_mul_scalar(cryptx_ecc_point*p, -constuint8_t*scalar, -size_tscalar_bit_width); - -#endif - -#endif + +enumCRYPTX_ASN1_TAGS{ +ASN1_RESVD=0, +ASN1_BOOLEAN, +ASN1_INTEGER, +ASN1_BITSTRING, +ASN1_OCTETSTRING, +ASN1_NULL, +ASN1_OBJECTID, +ASN1_OBJECTDESC, +ASN1_INSTANCE, +ASN1_REAL, +ASN1_ENUMERATED, +ASN1_EMBEDDEDPDV, +ASN1_UTF8STRING, +ASN1_RELATIVEOID, +ASN1_SEQUENCE=16, +ASN1_SET, +ASN1_NUMERICSTRING, +ASN1_PRINTABLESTRING, +ASN1_TELETEXSTRING, +ASN1_VIDEOTEXSTRING, +ASN1_IA5STRING, +ASN1_UTCTIME, +ASN1_GENERALIZEDTIME, +ASN1_GRAPHICSTRING, +ASN1_VISIBLESTRING, +ASN1_GENERALSTRING, +ASN1_UNIVERSALSTRING, +ASN1_CHARSTRING, +ASN1_BMPSTRING +}; + +enumCRYPTX_ASN1_CLASSES{ +ASN1_UNIVERSAL, +ASN1_APPLICATION, +ASN1_CONTEXTSPEC, +ASN1_PRIVATE +}; + +enumCRYPTX_ASN1_FORMS{ +ASN1_PRIMITIVE, +ASN1_CONSTRUCTED, +}; + +#definecryptx_asn1_get_tag(tag)((tag)&0b111111) +#definecryptx_asn1_get_class(tag)(((tag)>>6)&0b11) +#definecryptx_asn1_get_form(tag)(((tag)>>5)&1) + +typedefenum{ +ASN1_OK, +ASN1_END_OF_FILE, +ASN1_INVALID_ARG, +ASN1_LEN_OVERFLOW, +}asn1_error_t; + +asn1_error_tcryptx_asn1_decode( +void*data_start, +size_tdata_len, +uint8_tseek_to, +uint8_t*element_tag, +size_t*element_len, +uint8_t**element_data); + + + +#definecryptx_base64_get_encoded_len(len)((len)*4/3) +#definecryptx_base64_get_decoded_len(len)((len)*3/4) + +size_tcryptx_base64_encode(void*dest,constvoid*src,size_tlen); + +size_tcryptx_base64_decode(void*dest,constvoid*src,size_tlen); + + +#ifdefCRYPTX_ENABLE_HAZMAT + +voidcryptx_hazmat_aes_ecb_encrypt(constvoid*block_in, +void*block_out, +structcryptx_aes_ctx*ks); + +voidcryptx_hazmat_aes_ecb_decrypt(constvoid*block_in, +void*block_out, +structcryptx_aes_ctx*ks); + +boolcryptx_hazmat_rsa_oaep_encode(constvoid*plaintext, +size_tlen, +void*encoded, +size_tmodulus_len, +constuint8_t*auth, +uint8_thash_alg); + +boolcryptx_hazmat_rsa_oaep_decode(constvoid*encoded, +size_tlen, +void*plaintext, +constuint8_t*auth, +uint8_thash_alg); + +voidcryptx_hazmat_powmod(uint8_tsize,uint8_t*restrictbase,uint24_texp,constuint8_t*restrictmod); + +#defineCRYPTX_GF2_INTLEN30 + +structcryptx_ecc_point{ +uint8_tx[CRYPTX_GF2_INTLEN]; +uint8_ty[CRYPTX_GF2_INTLEN]; +} + +voidcryptx_hazmat_ecc_point_add(cryptx_ecc_point*p,cryptx_ecc_point*q); + +voidcryptx_hazmat_ecc_point_double(cryptx_ecc_point*p); + +voidcryptx_hazmat_ecc_point_mul_scalar(cryptx_ecc_point*p, +constuint8_t*scalar, +size_tscalar_bit_width); + +#endif + +#endif diff --git a/docs/doxygen/xml/structCipher.xml b/docs/doxygen/xml/structCipher.xml index f7061736..ce1f0a49 100644 --- a/docs/doxygen/xml/structCipher.xml +++ b/docs/doxygen/xml/structCipher.xml @@ -10,7 +10,7 @@ for AES - + diff --git a/docs/doxygen/xml/structcryptx__aes__ctx.xml b/docs/doxygen/xml/structcryptx__aes__ctx.xml index a6c271ec..42a606d1 100644 --- a/docs/doxygen/xml/structcryptx__aes__ctx.xml +++ b/docs/doxygen/xml/structcryptx__aes__ctx.xml @@ -16,7 +16,7 @@ - + uint32_t @@ -31,7 +31,7 @@ - + uint8_t @@ -46,7 +46,7 @@ - + uint8_t @@ -61,7 +61,7 @@ - + uint8_t @@ -76,7 +76,7 @@ - + cryptx_aes_private_h @@ -91,7 +91,7 @@ - + @@ -131,7 +131,7 @@ - + cryptx_aes_ctxciphermode cryptx_aes_ctxiv diff --git a/docs/doxygen/xml/structcryptx__ecc__point.xml b/docs/doxygen/xml/structcryptx__ecc__point.xml index a2e385e8..23e26e21 100644 --- a/docs/doxygen/xml/structcryptx__ecc__point.xml +++ b/docs/doxygen/xml/structcryptx__ecc__point.xml @@ -16,7 +16,7 @@ - + uint8_t @@ -30,7 +30,7 @@ - + @@ -38,7 +38,7 @@ - + cryptx_ecc_pointx cryptx_ecc_pointy diff --git a/docs/modules/aes.rst b/docs/modules/aes.rst index 14f08be1..c25058ea 100644 --- a/docs/modules/aes.rst +++ b/docs/modules/aes.rst @@ -6,7 +6,7 @@ Advanced Encryption Standard .. raw:: html -

Module Functionality
Provides a fast, secure algorithm for two parties to exchange information privately using a single key for encryption and decryption. Advanced Encryption Standard is currently the gold standard for encryption and is used all over the place.

+

Module Functionality
Provides a fast, secure algorithm for two parties to exchange information privately using a single key for encryption and decryption. Advanced Encryption Standard is currently the gold standard for encryption and is one of the most widely-used encryption algorithms.

Cipher Modes ________________ @@ -81,19 +81,54 @@ The following functions are only valid for Galois Counter Mode (GCM). Attempting .. doxygenfunction:: cryptx_aes_verify :project: CryptX +There are also some enforced constraints on when these functions can be called, intended to prevent undefined behavior as well as to close a particularly nasty tag-forgery vulnerability [#f1]_ in GCM. -Additional Notes -__________________ ++----------------------------------------------------------------------------------------+ +| GCM FUNCTION VALIDITY CONTROL FLOW | ++-----------------------+-----------------------+--------------------+-------------------+ +| After Function Call | cryptx_aes_update_aad | cryptx_aes_encrypt | cryptx_aes_digest | ++=======================+=======================+====================+===================+ +| cryptx_aes_init | VALID | VALID | VALID | ++-----------------------+-----------------------+--------------------+-------------------+ +| cryptx_aes_update_aad | VALID | VALID | VALID | ++-----------------------+-----------------------+--------------------+-------------------+ +| cryptx_aes_encrypt | INVALID | VALID | VALID | ++-----------------------+-----------------------+--------------------+-------------------+ +| cryptx_aes_digest | INVALID | INVALID | INVALID | ++-----------------------+-----------------------+--------------------+-------------------+ -.. warning:: +.. _aes_iv_req: - * Cycle your AES key after encrypting 2^64 blocks of data with the same key. If you ever reach this on a freaking calculator I will literally send you some kind of metal. - .. |br|:: - * CBC and CTR modes by themselves ensure confidentiality but do not provide any assurances of message integrity or authenticity. If you need a truly secure construction, use GCM mode or append a keyed hash (HMAC) to the encrypted message. - .. |br|:: - * **GCM-Specific**: The context maintains flags to determine what functions are valid to call for the current state. When the context is first initialized any of the other AES functions are valid. This can be *cryptx_aes_update_aad* or *cryptx_aes_encrypt*. However, once the first call to encrypt occurs, you can no longer call update_aad. Once you call *cryptx_aes_digest* to return an authentication tag for the message, the context can no longer be used, period. This is an implementation detail meant to prevent the *GCM nonce-misuse/forbidden attack* vulnerability [CIT1]_. You will need to initialize the context (you can reuse the existing context) again with the same key but use a different IV. - +Initialization Vector Requirements +______________________________________ + +- **CBC Mode** + + | Requirement: Initialization vector must be securely-random. + | Non-Compliance Effect: Vulnerability to chosen plaintext attack [#f2]_. + | Assurance: Generate a random IV with :code:`cryptx_csrand_fill` for use with this mode. +- **CTR & GCM Modes** + + | Requirement: Counter portion of initialization vector must be unique (not re-used). + | Non-Compliance Effect: Vulnerability to many-time pad [#f3]_. + | Additional Options: A fixed nonce may preceed the counter portion of the IV. This should be securely random. Default configuration for CTR mode is an 8 byte nonce followed by an 8 byte counter, though this can be configured during cipher initialization. + | Assurance: Fill the nonce portion of your IV like you would with CBC mode. This may be repeated for the counter portion or counter may start at 0. + + +Notes +_______ + +(1) The initialization vector used for the cipher state (or message for GCM mode) may be communicated to the other party as the first block of the ciphertext. + +(2) The AES cipher begins to leak information after a certain number of blocks have been encrypted under a single key. This number differs by cipher mode but can range anywhere from :code:`2 ^ 48` to :code:`2^64` blocks of data. This is a stupidly large amount of data that you will never realistically reach. + +(3) CBC and CTR modes by themselves ensure confidentiality but do not provide any assurances of message integrity or authenticity. If you need a truly secure construction, use GCM mode or append a keyed hash (HMAC) to the encrypted message. +---- -.. [CIT1] https://csrc.nist.gov/csrc/media/Projects/crypto-publication-review-project/documents/initial-comments/sp800-38d-initial-public-comments-2021.pdf, Page 2 +.. [#f1] **GCM Nonce-Misuse/Forbidden Attack Vulnerability**. It involves the leaking of bits of the hash subkey used to generate the authentication tag if the same initialization vector is used to authenticate multiple messages. This allows an attacker to embed a valid signature for an altered message. To resolve this vulnerability within this GCM implementation call :code:`cryptx_aes_init` again with a new initialization vector after you return a digest for a data stream. For more details on this vulnerability `click here `_. + +.. [#f2] **Chosen Plaintext Attack**. An attack against a cryptosystem involving requesting multiple encryptions while controlling bits of the input plaintext. This allows an attacker to reveal bits of the encryption secret. To resolve this vulnerability the output of an encryption algorithm needs to be securely random. See :ref:`aes_iv_req`. + +.. [#f3] **Many-Time Pad**. This vulnerability derived from the **One-Time Pad** algorithm which was one of the first encryption algorithms developed. It involved XOR'ing a message with a key of equal length and had perfect secrecy. Issues arose with this algorithm if the key began to repeat, which would reveal the plaintext given only a few ciphertexts. AES CTR and GCM modes use the counter block within the IV to generate a one-time pad and therefore are subject to this vulnerability. To resolve this vulnerability ensure that you do not allow your counter block to repeat under the same key. diff --git a/docs/www/_sources/modules/aes.rst.txt b/docs/www/_sources/modules/aes.rst.txt index 14f08be1..c25058ea 100644 --- a/docs/www/_sources/modules/aes.rst.txt +++ b/docs/www/_sources/modules/aes.rst.txt @@ -6,7 +6,7 @@ Advanced Encryption Standard .. raw:: html -

Module Functionality
Provides a fast, secure algorithm for two parties to exchange information privately using a single key for encryption and decryption. Advanced Encryption Standard is currently the gold standard for encryption and is used all over the place.

+

Module Functionality
Provides a fast, secure algorithm for two parties to exchange information privately using a single key for encryption and decryption. Advanced Encryption Standard is currently the gold standard for encryption and is one of the most widely-used encryption algorithms.

Cipher Modes ________________ @@ -81,19 +81,54 @@ The following functions are only valid for Galois Counter Mode (GCM). Attempting .. doxygenfunction:: cryptx_aes_verify :project: CryptX +There are also some enforced constraints on when these functions can be called, intended to prevent undefined behavior as well as to close a particularly nasty tag-forgery vulnerability [#f1]_ in GCM. -Additional Notes -__________________ ++----------------------------------------------------------------------------------------+ +| GCM FUNCTION VALIDITY CONTROL FLOW | ++-----------------------+-----------------------+--------------------+-------------------+ +| After Function Call | cryptx_aes_update_aad | cryptx_aes_encrypt | cryptx_aes_digest | ++=======================+=======================+====================+===================+ +| cryptx_aes_init | VALID | VALID | VALID | ++-----------------------+-----------------------+--------------------+-------------------+ +| cryptx_aes_update_aad | VALID | VALID | VALID | ++-----------------------+-----------------------+--------------------+-------------------+ +| cryptx_aes_encrypt | INVALID | VALID | VALID | ++-----------------------+-----------------------+--------------------+-------------------+ +| cryptx_aes_digest | INVALID | INVALID | INVALID | ++-----------------------+-----------------------+--------------------+-------------------+ -.. warning:: +.. _aes_iv_req: - * Cycle your AES key after encrypting 2^64 blocks of data with the same key. If you ever reach this on a freaking calculator I will literally send you some kind of metal. - .. |br|:: - * CBC and CTR modes by themselves ensure confidentiality but do not provide any assurances of message integrity or authenticity. If you need a truly secure construction, use GCM mode or append a keyed hash (HMAC) to the encrypted message. - .. |br|:: - * **GCM-Specific**: The context maintains flags to determine what functions are valid to call for the current state. When the context is first initialized any of the other AES functions are valid. This can be *cryptx_aes_update_aad* or *cryptx_aes_encrypt*. However, once the first call to encrypt occurs, you can no longer call update_aad. Once you call *cryptx_aes_digest* to return an authentication tag for the message, the context can no longer be used, period. This is an implementation detail meant to prevent the *GCM nonce-misuse/forbidden attack* vulnerability [CIT1]_. You will need to initialize the context (you can reuse the existing context) again with the same key but use a different IV. - +Initialization Vector Requirements +______________________________________ + +- **CBC Mode** + + | Requirement: Initialization vector must be securely-random. + | Non-Compliance Effect: Vulnerability to chosen plaintext attack [#f2]_. + | Assurance: Generate a random IV with :code:`cryptx_csrand_fill` for use with this mode. +- **CTR & GCM Modes** + + | Requirement: Counter portion of initialization vector must be unique (not re-used). + | Non-Compliance Effect: Vulnerability to many-time pad [#f3]_. + | Additional Options: A fixed nonce may preceed the counter portion of the IV. This should be securely random. Default configuration for CTR mode is an 8 byte nonce followed by an 8 byte counter, though this can be configured during cipher initialization. + | Assurance: Fill the nonce portion of your IV like you would with CBC mode. This may be repeated for the counter portion or counter may start at 0. + + +Notes +_______ + +(1) The initialization vector used for the cipher state (or message for GCM mode) may be communicated to the other party as the first block of the ciphertext. + +(2) The AES cipher begins to leak information after a certain number of blocks have been encrypted under a single key. This number differs by cipher mode but can range anywhere from :code:`2 ^ 48` to :code:`2^64` blocks of data. This is a stupidly large amount of data that you will never realistically reach. + +(3) CBC and CTR modes by themselves ensure confidentiality but do not provide any assurances of message integrity or authenticity. If you need a truly secure construction, use GCM mode or append a keyed hash (HMAC) to the encrypted message. +---- -.. [CIT1] https://csrc.nist.gov/csrc/media/Projects/crypto-publication-review-project/documents/initial-comments/sp800-38d-initial-public-comments-2021.pdf, Page 2 +.. [#f1] **GCM Nonce-Misuse/Forbidden Attack Vulnerability**. It involves the leaking of bits of the hash subkey used to generate the authentication tag if the same initialization vector is used to authenticate multiple messages. This allows an attacker to embed a valid signature for an altered message. To resolve this vulnerability within this GCM implementation call :code:`cryptx_aes_init` again with a new initialization vector after you return a digest for a data stream. For more details on this vulnerability `click here `_. + +.. [#f2] **Chosen Plaintext Attack**. An attack against a cryptosystem involving requesting multiple encryptions while controlling bits of the input plaintext. This allows an attacker to reveal bits of the encryption secret. To resolve this vulnerability the output of an encryption algorithm needs to be securely random. See :ref:`aes_iv_req`. + +.. [#f3] **Many-Time Pad**. This vulnerability derived from the **One-Time Pad** algorithm which was one of the first encryption algorithms developed. It involved XOR'ing a message with a key of equal length and had perfect secrecy. Issues arose with this algorithm if the key began to repeat, which would reveal the plaintext given only a few ciphertexts. AES CTR and GCM modes use the counter block within the IV to generate a one-time pad and therefore are subject to this vulnerability. To resolve this vulnerability ensure that you do not allow your counter block to repeat under the same key. diff --git a/docs/www/modules/aes.html b/docs/www/modules/aes.html index bda18021..2b7f8978 100644 --- a/docs/www/modules/aes.html +++ b/docs/www/modules/aes.html @@ -15,7 +15,6 @@ - @@ -49,7 +48,8 @@
  • Macros
  • Response Codes
  • Functions
  • -
  • Additional Notes
  • +
  • Initialization Vector Requirements
  • +
  • Notes
  • Analysis & Overview
  • @@ -80,7 +80,7 @@

    Advanced Encryption Standard

    -

    Module Functionality
    Provides a fast, secure algorithm for two parties to exchange information privately using a single key for encryption and decryption. Advanced Encryption Standard is currently the gold standard for encryption and is used all over the place.

    +

    Module Functionality
    Provides a fast, secure algorithm for two parties to exchange information privately using a single key for encryption and decryption. Advanced Encryption Standard is currently the gold standard for encryption and is one of the most widely-used encryption algorithms.

    Cipher Modes

    @@ -284,18 +284,6 @@

    Functions aes_error_t cryptx_aes_encrypt(const struct cryptx_aes_ctx *context, const void *plaintext, size_t len, void *ciphertext)

    Performs a stateful AES encryption of an arbitrary length of data.

    -
    -

    Note

    -

    ciphertext should large enough to hold the encrypted message. See the cryptx_aes_get_ciphertext_len macro for CBC mode.

    -
    -
    -

    Note

    -

    plaintext and ciphertext are aliasable.

    -
    -
    -

    Note

    -

    Encrypt is streamable, such that \( encrypt(msg1) + encrypt(msg2) \) is functionally identical to \( encrypt(msg1+msg2) \) with the exception of intervening padding in CBC mode.

    -
    Parameters
      @@ -315,14 +303,6 @@

      Functions aes_error_t cryptx_aes_decrypt(const struct cryptx_aes_ctx *context, const void *ciphertext, size_t len, void *plaintext)

      Performs a stateful AES decryption of an arbitrary length of data.

      -
      -

      Note

      -

      plaintext and ciphertext are aliasable.

      -
      -
      -

      Note

      -

      Decrypt is streamable, such that \( decrypt(msg1) + decrypt(msg2) \) is functionally identical to \( decrypt(msg1+msg2) \) with the exception of intervening padding in CBC mode.

      -
      Parameters
        @@ -345,10 +325,6 @@

        Functionsaes_error_t cryptx_aes_update_aad(struct cryptx_aes_ctx *context, const void *aad, size_t aad_len)

        Updates the cipher context for given AAD (Additional Authenticated Data).

        AAD is data that is only authenticated, not encrypted.

        -
        -

        Note

        -

        This function can only be called between cipher initialization and encryption/decryption. Attempting to call this function at any other time will return AES_INVALID_OPERATION.

        -
        Parameters
          @@ -367,10 +343,6 @@

          Functions aes_error_t cryptx_aes_digest(struct cryptx_aes_ctx *context, uint8_t *digest)

          Returns the current authentication tag for data parsed so far.

          -
          -

          Warning

          -

          Nonce-misuse vulnerability/forbidden attack: GCM is vulnerable to key discovery if the same nonce is reused to encrypt or decrypt multiple messages. Once you generate a digest for data processed by the cipher, cycle the nonce by generating a new one and calling cryptx_aes_init again with the new nonce. To ensure this is done properly, the context is marked invalid once this function is called.

          -
          Parameters

    -
    -

    Additional Notes

    -
    -

    Warning

    -
      -
    • Cycle your AES key after encrypting 2^64 blocks of data with the same key. If you ever reach this on a freaking calculator I will literally send you some kind of metal.

    • -
    -
      -
    • CBC and CTR modes by themselves ensure confidentiality but do not provide any assurances of message integrity or authenticity. If you need a truly secure construction, use GCM mode or append a keyed hash (HMAC) to the encrypted message.

    • -
    -
      -
    • GCM-Specific: The context maintains flags to determine what functions are valid to call for the current state. When the context is first initialized any of the other AES functions are valid. This can be cryptx_aes_update_aad or cryptx_aes_encrypt. However, once the first call to encrypt occurs, you can no longer call update_aad. Once you call cryptx_aes_digest to return an authentication tag for the message, the context can no longer be used, period. This is an implementation detail meant to prevent the GCM nonce-misuse/forbidden attack vulnerability [CIT1]. You will need to initialize the context (you can reuse the existing context) again with the same key but use a different IV.

    • +
      +

      Initialization Vector Requirements

      +
        +
      • CBC Mode

        +
        +
        Requirement: Initialization vector must be securely-random.
        +
        Non-Compliance Effect: Vulnerability to chosen plaintext attack 2.
        +
        Assurance: Generate a random IV with cryptx_csrand_fill for use with this mode.
        +
        +
      • +
      • CTR & GCM Modes

        +
        +
        Requirement: Counter portion of initialization vector must be unique (not re-used).
        +
        Non-Compliance Effect: Vulnerability to many-time pad 3.
        +
        Additional Options: A fixed nonce may preceed the counter portion of the IV. This should be securely random. Default configuration for CTR mode is an 8 byte nonce followed by an 8 byte counter, though this can be configured during cipher initialization.
        +
        Assurance: Fill the nonce portion of your IV like you would with CBC mode. This may be repeated for the counter portion or counter may start at 0.
        +
        +
      -
      -
      CIT1
      -

      https://csrc.nist.gov/csrc/media/Projects/crypto-publication-review-project/documents/initial-comments/sp800-38d-initial-public-comments-2021.pdf, Page 2

      +
      +

      Notes

      +
        +
      1. The initialization vector used for the cipher state (or message for GCM mode) may be communicated to the other party as the first block of the ciphertext.

      2. +
      3. The AES cipher begins to leak information after a certain number of blocks have been encrypted under a single key. This number differs by cipher mode but can range anywhere from 2 ^ 48 to 2^64 blocks of data. This is a stupidly large amount of data that you will never realistically reach.

      4. +
      5. CBC and CTR modes by themselves ensure confidentiality but do not provide any assurances of message integrity or authenticity. If you need a truly secure construction, use GCM mode or append a keyed hash (HMAC) to the encrypted message.

      6. +
      +
      +
      +
      1
      +

      GCM Nonce-Misuse/Forbidden Attack Vulnerability. It involves the leaking of bits of the hash subkey used to generate the authentication tag if the same initialization vector is used to authenticate multiple messages. This allows an attacker to embed a valid signature for an altered message. To resolve this vulnerability within this GCM implementation call cryptx_aes_init again with a new initialization vector after you return a digest for a data stream. For more details on this vulnerability click here.

      +
      +
      2
      +

      Chosen Plaintext Attack. An attack against a cryptosystem involving requesting multiple encryptions while controlling bits of the input plaintext. This allows an attacker to reveal bits of the encryption secret. To resolve this vulnerability the output of an encryption algorithm needs to be securely random. See Initialization Vector Requirements.

      +
      +
      3
      +

      Many-Time Pad. This vulnerability derived from the One-Time Pad algorithm which was one of the first encryption algorithms developed. It involved XOR’ing a message with a key of equal length and had perfect secrecy. Issues arose with this algorithm if the key began to repeat, which would reveal the plaintext given only a few ciphertexts. AES CTR and GCM modes use the counter block within the IV to generate a one-time pad and therefore are subject to this vulnerability. To resolve this vulnerability ensure that you do not allow your counter block to repeat under the same key.

      diff --git a/docs/www/modules/hmac.html b/docs/www/modules/hmac.html index a289ac8e..1db7a533 100644 --- a/docs/www/modules/hmac.html +++ b/docs/www/modules/hmac.html @@ -153,10 +153,6 @@

      Functions void cryptx_hmac_digest(struct cryptx_hmac_ctx *context, void *digest)

      Output digest for current HMAC-state (preserves state).

      -
      -

      Note

      -

      digest must be at large enough to hold the hash digest. You can retrieve the necessary size by accessing the digest_len member of an initialized cryptx_hmac_ctx.

      -
      Parameters
        diff --git a/docs/www/objects.inv b/docs/www/objects.inv index 3c395a4b341b7fab1ce1725ce826b195d28e2e3f..63c8f0a8695edd8db2286b034e6b97dde170e0a5 100644 GIT binary patch delta 6046 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 delta 6048 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