diff --git a/fizz/crypto/aead/AEGISCipher.cpp b/fizz/crypto/aead/AEGISCipher.cpp index af1ba3052e3..8e796e07627 100644 --- a/fizz/crypto/aead/AEGISCipher.cpp +++ b/fizz/crypto/aead/AEGISCipher.cpp @@ -97,43 +97,70 @@ std::unique_ptr aegisEncrypt( } folly::Optional> aegisDecrypt( - AEGISCipher::DecryptFn decrypt, + AEGISCipher::InitStateFn initstate, + AEGISCipher::AadUpdateFn aadUpdate, + AEGISCipher::AadFinalFn aadFinal, + AEGISCipher::DecryptUpdateFn decryptUpdate, + AEGISCipher::DecryptFinalFn decryptFinal, + AEGISCipher::AegisEVPCtx ctx, std::unique_ptr&& ciphertext, const folly::IOBuf* associatedData, folly::ByteRange iv, - size_t tagLen, - folly::ByteRange key) { - folly::ByteRange input = ciphertext->coalesce(); + folly::ByteRange key, + folly::MutableByteRange tagOut) { + if (initstate(key.data(), iv.data(), &ctx) != 0) { + throw std::runtime_error("Initiate encryption state error"); + } + auto inputLength = ciphertext->computeChainDataLength(); + folly::IOBuf* input; std::unique_ptr output; - output = folly::IOBuf::create(input.size() - tagLen); - output->append(input.size() - tagLen); + output = folly::IOBuf::create(inputLength); + output->append(inputLength); + input = ciphertext.get(); - const unsigned char* ad; - unsigned long long adlen; + unsigned long long adlen = 0; if (associatedData) { - if (associatedData->isChained()) { - throw std::overflow_error("associated data is chained or null"); + for (auto current : *associatedData) { + if (current.size() > std::numeric_limits::max()) { + throw std::runtime_error("too much associated data"); + } + if (aadUpdate(current.data(), current.size(), &ctx) != 0) { + throw std::runtime_error("Decryption aad update error"); + } + adlen += current.size(); + } + if (aadFinal(&ctx) != 0) { + throw std::runtime_error("Decryption aad final error"); } - ad = associatedData->data(); - adlen = associatedData->length(); - } else { - ad = nullptr; - adlen = 0; } - unsigned long long decryptedLength; - if (decrypt( - output->writableData(), - &decryptedLength, - nullptr, - input.data(), - input.size(), - ad, + unsigned long long writtenlen = 0; + unsigned long long totalWritten = 0; + for (auto current : *input) { + if (current.size() > std::numeric_limits::max()) { + throw std::runtime_error("too much plaintext data"); + } + if (decryptUpdate( + output->writableData() + totalWritten, + &writtenlen, + current.data(), + current.size(), + &ctx) != 0) { + throw std::runtime_error("Decryption update error"); + } + totalWritten += writtenlen; + } + + if (decryptFinal( + output->writableData() + totalWritten, + &writtenlen, + inputLength, adlen, - iv.data(), - key.data()) != 0) { - return folly::none; + tagOut.data(), + &ctx) != 0 || + totalWritten + writtenlen != inputLength) { + throw std::runtime_error("Decryption error"); } return output; } @@ -147,6 +174,8 @@ AEGISCipher::AEGISCipher( AadFinalFn addFinal, EncryptUpdateFn encryptUpdate, EncryptFinalFn encryptFinal, + DecryptUpdateFn decryptUpdate, + DecryptFinalFn decryptFinal, size_t keyLength, size_t ivLength, size_t tagLength) @@ -156,6 +185,8 @@ AEGISCipher::AEGISCipher( aadFinal_(addFinal), encryptUpdate_(encryptUpdate), encryptFinal_(encryptFinal), + decryptUpdate_(decryptUpdate), + decryptFinal_(decryptFinal), ctx_({}), keyLength_(keyLength), ivLength_(ivLength), @@ -182,6 +213,8 @@ std::unique_ptr AEGISCipher::make128L() { aegis128l_aad_final, aegis128l_encrypt_update, aegis128l_encrypt_final, + aegis128l_decrypt_update, + aegis128l_decrypt_final, fizz_aegis128l_KEYBYTES, fizz_aegis128l_NPUBBYTES, fizz_aegis128l_ABYTES)); @@ -195,6 +228,8 @@ std::unique_ptr AEGISCipher::make256() { aegis256_aad_final, aegis256_encrypt_update, aegis256_encrypt_final, + aegis256_decrypt_update, + aegis256_decrypt_final, fizz_aegis256_KEYBYTES, fizz_aegis256_NPUBBYTES, fizz_aegis256_ABYTES)); @@ -283,13 +318,37 @@ folly::Optional> AEGISCipher::tryDecrypt( const folly::IOBuf* associatedData, folly::ByteRange nonce, Aead::AeadOptions /*options*/) const { + if (tagLength_ > ciphertext->computeChainDataLength()) { + return folly::none; + } + + // Set up the tag buffer now + const auto& lastBuf = ciphertext->prev(); + folly::MutableByteRange tagOut; + if (lastBuf->length() >= tagLength_) { + // We can directly carve out this buffer from the last IOBuf + // Adjust buffer sizes + lastBuf->trimEnd(tagLength_); + + tagOut = {lastBuf->writableTail(), tagLength_}; + } else { + std::array tag; + // buffer to copy the tag into when we decrypt + tagOut = {tag.data(), tagLength_}; + trimBytes(*ciphertext, tagOut); + } return aegisDecrypt( - decrypt_, + initstate_, + aadUpdate_, + aadFinal_, + decryptUpdate_, + decryptFinal_, + ctx_, std::move(ciphertext), associatedData, nonce, - tagLength_, - trafficKeyKey_); + trafficKeyKey_, + tagOut); } size_t AEGISCipher::getCipherOverhead() const { diff --git a/fizz/crypto/aead/AEGISCipher.h b/fizz/crypto/aead/AEGISCipher.h index 8143bc60aeb..146defdb938 100644 --- a/fizz/crypto/aead/AEGISCipher.h +++ b/fizz/crypto/aead/AEGISCipher.h @@ -66,8 +66,22 @@ class AEGISCipher : public Aead { unsigned long long mlen, unsigned long long adlen, AegisEVPCtx* ctx); + using DecryptUpdateFn = int (*const)( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + AegisEVPCtx* ctx); + using DecryptFinalFn = int (*const)( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + AegisEVPCtx* ctx); static constexpr size_t kMaxIVLength = 32; + static constexpr size_t kTagLength = 16; static std::unique_ptr make128L(); static std::unique_ptr make256(); @@ -127,6 +141,8 @@ class AEGISCipher : public Aead { AadFinalFn aadFinal_, EncryptUpdateFn encryptUpdate, EncryptFinalFn encryptFinal, + DecryptUpdateFn decryptUpdate, + DecryptFinalFn decryptFinal, size_t keyLength, size_t ivLength, size_t tagLength); @@ -143,6 +159,8 @@ class AEGISCipher : public Aead { AadFinalFn aadFinal_; EncryptUpdateFn encryptUpdate_; EncryptFinalFn encryptFinal_; + DecryptUpdateFn decryptUpdate_; + DecryptFinalFn decryptFinal_; AegisEVPCtx ctx_; size_t keyLength_; size_t ivLength_; diff --git a/fizz/crypto/aead/test/AEGISCipherTest.cpp b/fizz/crypto/aead/test/AEGISCipherTest.cpp index 79289c88c5f..e505b621712 100644 --- a/fizz/crypto/aead/test/AEGISCipherTest.cpp +++ b/fizz/crypto/aead/test/AEGISCipherTest.cpp @@ -118,7 +118,29 @@ TEST_P(AEGISCipherTest, TestEncryptChunkedInput) { } } -// Adapted from libsodium's aegis 128l testing values +TEST_P(AEGISCipherTest, TestDecryptChunkedInput) { + std::unique_ptr cipher = getTestCipher(GetParam()); + auto origLength = toIOBuf(GetParam().ciphertext)->computeChainDataLength(); + auto aad = toIOBuf(GetParam().aad); + auto nonce_iobuf = toIOBuf(GetParam().iv); + auto nonce = folly::ByteRange(nonce_iobuf->data(), nonce_iobuf->length()); + for (size_t i = 2; i < origLength; i++) { + auto ciphertext = toIOBuf(GetParam().ciphertext); + auto chunkedInput = chunkIOBuf(std::move(ciphertext), i); + try { + auto out = cipher->decrypt(std::move(chunkedInput), aad.get(), nonce); + bool valid = IOBufEqualTo()(toIOBuf(GetParam().plaintext), out); + EXPECT_EQ(valid, GetParam().valid); + EXPECT_EQ( + out->computeChainDataLength(), + origLength - cipher->getCipherOverhead()); + } catch (const std::runtime_error&) { + EXPECT_FALSE(GetParam().valid); + } + } +} + +// Adapted from libsodium's aegis testing values INSTANTIATE_TEST_SUITE_P( AEGIS128LTestVectors, AEGISCipherTest, diff --git a/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.c b/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.c index 2abebad25ea..c3a4d3d5889 100644 --- a/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.c +++ b/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.c @@ -155,6 +155,25 @@ int aegis128l_encrypt_final( return aegis_evp->encrypt_final(c, c_writtenlen_p, mlen, adlen, ctx); } +int aegis128l_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) { + return aegis_evp->decrypt_update(m, outlen, c, clen, ctx); +} + +int aegis128l_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) { + return aegis_evp->decrypt_final(m, outlen, mlen, adlen, mac, ctx); +} + int fizz_aegis128l_pick_best_implementation(void) { diff --git a/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.h b/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.h index 626d1def8c4..484b1fdeaea 100644 --- a/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.h +++ b/fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.h @@ -37,6 +37,19 @@ typedef struct aegis128l_evp { unsigned long long mlen, unsigned long long adlen, fizz_aegis_evp_ctx* ctx); + int (*decrypt_update)( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx); + int (*decrypt_final)( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx); } aegis128l_evp; #endif diff --git a/fizz/third-party/libsodium-aegis/aegis128l/aesni/aead_aegis128l_aesni.c b/fizz/third-party/libsodium-aegis/aegis128l/aesni/aead_aegis128l_aesni.c index 0cb48597852..6f9f4850c11 100644 --- a/fizz/third-party/libsodium-aegis/aegis128l/aesni/aead_aegis128l_aesni.c +++ b/fizz/third-party/libsodium-aegis/aegis128l/aesni/aead_aegis128l_aesni.c @@ -379,6 +379,7 @@ static int aegis128l_encrypt_final( } aegis128l_mac(c + buffer_size, adlen, mlen, ctx->aegis128l.aesni_state); + sodium_memzero(ctx->aegis128l.aesni_state, sizeof ctx->aegis128l.aesni_state); // total final written length is the buffer length plus tag length if (outlen != NULL) { *outlen = (buffer_size + TAG_LEN); @@ -387,6 +388,91 @@ static int aegis128l_encrypt_final( return 0; } +static int aegis128l_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) { + unsigned long long i; + unsigned long long writtenlen = 0; + unsigned int buffer_size = ctx->aegis128l.buffer_size; + unsigned int copy_size = 0; + + // If buffer has existing bytes, copy m to buffer and update state if + // buffer is full + if (buffer_size > 0 && buffer_size < STATE_SIZE) { + unsigned int rem_buffer_size = STATE_SIZE - buffer_size; + copy_size = clen <= rem_buffer_size ? clen : rem_buffer_size; + memcpy(ctx->aegis128l.buffer + buffer_size, c, copy_size); + buffer_size += copy_size; + if (buffer_size == STATE_SIZE) { + aegis128l_dec(m, ctx->aegis128l.buffer, ctx->aegis128l.aesni_state); + buffer_size = 0; + writtenlen += STATE_SIZE; + memset(ctx->aegis128l.buffer, 0, STATE_SIZE); + } + } + + // decrypt full blocks (STATE_SIZE) worth of bytes + for (i = copy_size; i + STATE_SIZE <= clen; i += STATE_SIZE) { + aegis128l_dec(m + writtenlen, c + i, ctx->aegis128l.aesni_state); + writtenlen += STATE_SIZE; + } + + // Copy remaining bytes from c to buffer + unsigned int leftover = (clen - copy_size) & 0x1f; + if (leftover) { + memcpy(ctx->aegis128l.buffer, c + i, leftover); + buffer_size = leftover; + } + + if (outlen != NULL) { + *outlen = writtenlen; + } + ctx->aegis128l.buffer_size = buffer_size; + return 0; +} + +static int aegis128l_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) { + unsigned int buffer_size = ctx->aegis128l.buffer_size; + aes_block_t *const state = ctx->aegis128l.aesni_state; + CRYPTO_ALIGN(16) unsigned char computed_mac[16]; + int ret; + + if (buffer_size > 0) { + CRYPTO_ALIGN(16) unsigned char src[STATE_SIZE]; + CRYPTO_ALIGN(16) unsigned char dst[STATE_SIZE]; + memset(src, 0, STATE_SIZE); + memcpy(src, ctx->aegis128l.buffer, buffer_size); + aegis128l_dec(dst, src, state); + memcpy(m, dst, buffer_size); + memset(dst, 0, buffer_size); + state[0] = AES_BLOCK_XOR(state[0], AES_BLOCK_LOAD(dst)); + state[4] = AES_BLOCK_XOR(state[4], AES_BLOCK_LOAD(dst + 16)); + sodium_memzero(dst, sizeof dst); + sodium_memzero(src, sizeof src); + sodium_memzero(ctx->aegis128l.buffer, sizeof ctx->aegis128l.buffer); + ctx->aegis128l.buffer_size = 0; + } + + if (outlen != NULL) { + *outlen = buffer_size; + } + + aegis128l_mac(computed_mac, adlen, mlen, state); + ret = crypto_verify_16(computed_mac, mac); + sodium_memzero(computed_mac, sizeof computed_mac); + sodium_memzero(state, sizeof state); + return ret; +} + struct crypto_aead_aegis128l_implementation fizz_crypto_aead_aegis128l_aesni_implementation = { SODIUM_C99(.encrypt_detached =) aegis128l_encrypt_detached, SODIUM_C99(.decrypt_detached =) aegis128l_decrypt_detached @@ -398,6 +484,8 @@ struct aegis128l_evp aegis128l_aesni_evp = { SODIUM_C99(.aad_final =) aegis128l_aad_final, SODIUM_C99(.encrypt_update =) aegis128l_encrypt_update, SODIUM_C99(.encrypt_final =) aegis128l_encrypt_final, + SODIUM_C99(.decrypt_update =) aegis128l_decrypt_update, + SODIUM_C99(.decrypt_final =) aegis128l_decrypt_final, }; #endif diff --git a/fizz/third-party/libsodium-aegis/aegis128l/crypto_aead_aegis128l.h b/fizz/third-party/libsodium-aegis/aegis128l/crypto_aead_aegis128l.h index ba95cd4e9db..8c4dc2cc8cf 100644 --- a/fizz/third-party/libsodium-aegis/aegis128l/crypto_aead_aegis128l.h +++ b/fizz/third-party/libsodium-aegis/aegis128l/crypto_aead_aegis128l.h @@ -119,6 +119,23 @@ int aegis128l_encrypt_final( unsigned long long adlen, fizz_aegis_evp_ctx *ctx) __attribute__((nonnull(1, 5))); +SODIUM_EXPORT +int aegis128l_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) __attribute__((nonnull(1, 3, 5))); + +SODIUM_EXPORT +int aegis128l_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) __attribute__((nonnull(1, 5, 6))); + SODIUM_EXPORT int fizz_aegis128l_pick_best_implementation(); diff --git a/fizz/third-party/libsodium-aegis/aegis128l/soft/aead_aegis128l_soft.c b/fizz/third-party/libsodium-aegis/aegis128l/soft/aead_aegis128l_soft.c index d39c106471f..22081027134 100644 --- a/fizz/third-party/libsodium-aegis/aegis128l/soft/aead_aegis128l_soft.c +++ b/fizz/third-party/libsodium-aegis/aegis128l/soft/aead_aegis128l_soft.c @@ -385,6 +385,7 @@ static int aegis128l_encrypt_final( } aegis128l_mac(c + buffer_size, adlen, mlen, state); + sodium_memzero(state, sizeof state); // total final written length is the buffer length plus tag length if (outlen != NULL) { *outlen = (buffer_size + TAG_LEN); @@ -393,6 +394,92 @@ static int aegis128l_encrypt_final( return 0; } +static int aegis128l_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) { + unsigned long long i; + unsigned long long writtenlen = 0; + unsigned int buffer_size = ctx->aegis128l.buffer_size; + unsigned int copy_size = 0; + STATE ctx->aegis128l.soft_state; + + // If buffer has existing bytes, copy m to buffer and update state if + // buffer is full + if (buffer_size > 0 && buffer_size < STATE_SIZE) { + unsigned int rem_buffer_size = STATE_SIZE - buffer_size; + copy_size = clen <= rem_buffer_size ? clen : rem_buffer_size; + memcpy(ctx->aegis128l.buffer + buffer_size, c, copy_size); + buffer_size += copy_size; + if (buffer_size == STATE_SIZE) { + aegis128l_dec(m, ctx->aegis128l.buffer, state); + buffer_size = 0; + writtenlen += STATE_SIZE; + memset(ctx->aegis128l.buffer, 0, STATE_SIZE); + } + } + + // decrypt full blocks (STATE_SIZE) worth of bytes + for (i = copy_size; i + STATE_SIZE <= clen; i += STATE_SIZE) { + aegis128l_dec(m + writtenlen, c + i, state); + writtenlen += STATE_SIZE; + } + + // Copy remaining bytes from c to buffer + unsigned int leftover = (clen - copy_size) & 0x1f; + if (leftover) { + memcpy(ctx->aegis128l.buffer, c + i, leftover); + buffer_size = leftover; + } + + if (outlen != NULL) { + *outlen = writtenlen; + } + ctx->aegis128l.buffer_size = buffer_size; + return 0; +} + +static int aegis128l_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) { + unsigned int buffer_size = ctx->aegis128l.buffer_size; + CRYPTO_ALIGN(16) unsigned char computed_mac[16]; + STATE ctx->aegis128l.soft_state; + int ret; + + if (buffer_size > 0) { + CRYPTO_ALIGN(16) unsigned char src[STATE_SIZE]; + CRYPTO_ALIGN(16) unsigned char dst[STATE_SIZE]; + memset(src, 0, STATE_SIZE); + memcpy(src, ctx->aegis128l.buffer, buffer_size); + aegis128l_dec(dst, src, state); + memcpy(m, dst, buffer_size); + memset(dst, 0, buffer_size); + state[0] = AES_BLOCK_XOR(state[0], AES_BLOCK_LOAD(dst)); + state[4] = AES_BLOCK_XOR(state[4], AES_BLOCK_LOAD(dst + 16)); + sodium_memzero(dst, sizeof dst); + sodium_memzero(src, sizeof src); + sodium_memzero(ctx->aegis128l.buffer, sizeof ctx->aegis128l.buffer); + ctx->aegis128l.buffer_size = 0; + } + + if (outlen != NULL) { + *outlen = buffer_size; + } + + aegis128l_mac(computed_mac, adlen, mlen, state); + ret = crypto_verify_16(computed_mac, mac); + sodium_memzero(computed_mac, sizeof computed_mac); + sodium_memzero(state, sizeof state); + return ret; +} + struct crypto_aead_aegis128l_implementation fizz_crypto_aead_aegis128l_soft_implementation = { SODIUM_C99(.encrypt_detached =) aegis128l_encrypt_detached, SODIUM_C99(.decrypt_detached =) aegis128l_decrypt_detached @@ -404,4 +491,6 @@ struct aegis128l_evp aegis128l_soft_evp = { SODIUM_C99(.aad_final =) aegis128l_aad_final, SODIUM_C99(.encrypt_update =) aegis128l_encrypt_update, SODIUM_C99(.encrypt_final =) aegis128l_encrypt_final, + SODIUM_C99(.decrypt_update =) aegis128l_decrypt_update, + SODIUM_C99(.decrypt_final =) aegis128l_decrypt_final, }; diff --git a/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.c b/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.c index cc64cc71bad..11b55684c12 100644 --- a/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.c +++ b/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.c @@ -156,6 +156,26 @@ int aegis256_encrypt_final( return aegis_evp->encrypt_final(c, c_writtenlen_p, mlen, adlen, ctx); } +int aegis256_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) { + return aegis_evp->decrypt_update(m, outlen, c, clen, ctx); +} + +int aegis256_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) { + return aegis_evp->decrypt_final(m, outlen, mlen, adlen, mac, ctx); +} + + int fizz_aegis256_pick_best_implementation(void) { diff --git a/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.h b/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.h index 958cda52029..5caf0322992 100644 --- a/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.h +++ b/fizz/third-party/libsodium-aegis/aegis256/aead_aegis256.h @@ -37,6 +37,19 @@ typedef struct aegis256_evp { unsigned long long mlen, unsigned long long adlen, fizz_aegis_evp_ctx *ctx); + int (*decrypt_update)( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx); + int (*decrypt_final)( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx); } aegis256_evp; #endif diff --git a/fizz/third-party/libsodium-aegis/aegis256/aesni/aead_aegis256_aesni.c b/fizz/third-party/libsodium-aegis/aegis256/aesni/aead_aegis256_aesni.c index e3e3394ffa1..0c845ee31c2 100644 --- a/fizz/third-party/libsodium-aegis/aegis256/aesni/aead_aegis256_aesni.c +++ b/fizz/third-party/libsodium-aegis/aegis256/aesni/aead_aegis256_aesni.c @@ -366,6 +366,7 @@ static int aegis256_encrypt_final( } aegis256_mac(c + buffer_size, adlen, mlen, ctx->aegis256.aesni_state); + sodium_memzero(ctx->aegis256.aesni_state, sizeof ctx->aegis256.aesni_state); // total final written length is the buffer length plus tag length if (outlen != NULL) { *outlen = (buffer_size + TAG_LEN); @@ -374,6 +375,90 @@ static int aegis256_encrypt_final( return 0; } +static int aegis256_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) { + unsigned long long i; + unsigned long long writtenlen = 0; + unsigned int buffer_size = ctx->aegis256.buffer_size; + unsigned int copy_size = 0; + + // If buffer has existing bytes, copy m to buffer and update state if + // buffer is full + if (buffer_size > 0 && buffer_size < STATE_SIZE) { + unsigned int rem_buffer_size = STATE_SIZE - buffer_size; + copy_size = clen <= rem_buffer_size ? clen : rem_buffer_size; + memcpy(ctx->aegis256.buffer + buffer_size, c, copy_size); + buffer_size += copy_size; + if (buffer_size == STATE_SIZE) { + aegis256_dec(m, ctx->aegis256.buffer, ctx->aegis256.aesni_state); + buffer_size = 0; + writtenlen += STATE_SIZE; + memset(ctx->aegis256.buffer, 0, STATE_SIZE); + } + } + + // decrypt full blocks (STATE_SIZE) worth of bytes + for (i = copy_size; i + STATE_SIZE <= clen; i += STATE_SIZE) { + aegis256_dec(m + writtenlen, c + i, ctx->aegis256.aesni_state); + writtenlen += STATE_SIZE; + } + + // Copy remaining bytes from c to buffer + unsigned int leftover = (clen - copy_size) & 0xf; + if (leftover) { + memcpy(ctx->aegis256.buffer, c + i, leftover); + buffer_size = leftover; + } + + if (outlen != NULL) { + *outlen = writtenlen; + } + ctx->aegis256.buffer_size = buffer_size; + return 0; +} + +static int aegis256_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) { + unsigned int buffer_size = ctx->aegis256.buffer_size; + aes_block_t *const state = ctx->aegis256.aesni_state; + CRYPTO_ALIGN(16) unsigned char computed_mac[16]; + int ret; + + if (buffer_size > 0) { + CRYPTO_ALIGN(16) unsigned char src[STATE_SIZE]; + CRYPTO_ALIGN(16) unsigned char dst[STATE_SIZE]; + memset(src, 0, STATE_SIZE); + memcpy(src, ctx->aegis256.buffer, buffer_size); + aegis256_dec(dst, src, state); + memcpy(m, dst, buffer_size); + memset(dst, 0, buffer_size); + state[0] = AES_BLOCK_XOR(state[0], AES_BLOCK_LOAD(dst)); + sodium_memzero(dst, sizeof dst); + sodium_memzero(src, sizeof src); + sodium_memzero(ctx->aegis256.buffer, sizeof ctx->aegis256.buffer); + ctx->aegis256.buffer_size = 0; + } + + if (outlen != NULL) { + *outlen = buffer_size; + } + + aegis256_mac(computed_mac, adlen, mlen, state); + ret = crypto_verify_16(computed_mac, mac); + sodium_memzero(computed_mac, sizeof computed_mac); + sodium_memzero(state, sizeof state); + return ret; +} + struct crypto_aead_aegis256_implementation fizz_crypto_aead_aegis256_aesni_implementation = { SODIUM_C99(.encrypt_detached =) aegis256_encrypt_detached, SODIUM_C99(.decrypt_detached =) aegis256_decrypt_detached @@ -385,5 +470,7 @@ struct aegis256_evp aegis256_aesni_evp = { SODIUM_C99(.aad_final =) aegis256_aad_final, SODIUM_C99(.encrypt_update =) aegis256_encrypt_update, SODIUM_C99(.encrypt_final =) aegis256_encrypt_final, + SODIUM_C99(.decrypt_update =) aegis256_decrypt_update, + SODIUM_C99(.decrypt_final =) aegis256_decrypt_final, }; #endif diff --git a/fizz/third-party/libsodium-aegis/aegis256/crypto_aead_aegis256.h b/fizz/third-party/libsodium-aegis/aegis256/crypto_aead_aegis256.h index 632fd753dbb..27cb2602818 100644 --- a/fizz/third-party/libsodium-aegis/aegis256/crypto_aead_aegis256.h +++ b/fizz/third-party/libsodium-aegis/aegis256/crypto_aead_aegis256.h @@ -119,6 +119,23 @@ int aegis256_encrypt_final( unsigned long long adlen, fizz_aegis_evp_ctx* ctx) __attribute__((nonnull(1, 5))); +SODIUM_EXPORT +int aegis256_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) __attribute__((nonnull(1, 3, 5))); + +SODIUM_EXPORT +int aegis256_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) __attribute__((nonnull(1, 5, 6))); + SODIUM_EXPORT int fizz_aegis256_pick_best_implementation(void); diff --git a/fizz/third-party/libsodium-aegis/aegis256/soft/aead_aegis256_soft.c b/fizz/third-party/libsodium-aegis/aegis256/soft/aead_aegis256_soft.c index 5d2fb9818b3..4b61f27ce5e 100644 --- a/fizz/third-party/libsodium-aegis/aegis256/soft/aead_aegis256_soft.c +++ b/fizz/third-party/libsodium-aegis/aegis256/soft/aead_aegis256_soft.c @@ -373,6 +373,7 @@ static int aegis256_encrypt_final( } aegis256_mac(c + buffer_size, adlen, mlen, state); + sodium_memzero(state, sizeof state); // total final written length is the buffer length plus tag length if (outlen != NULL) { *outlen = (buffer_size + TAG_LEN); @@ -381,6 +382,91 @@ static int aegis256_encrypt_final( return 0; } +static int aegis256_decrypt_update( + unsigned char* m, + unsigned long long* outlen, + const unsigned char* c, + unsigned long long clen, + fizz_aegis_evp_ctx* ctx) { + unsigned long long i; + unsigned long long writtenlen = 0; + unsigned int buffer_size = ctx->aegis256.buffer_size; + unsigned int copy_size = 0; + STATE ctx->aegis256.soft_state; + + // If buffer has existing bytes, copy m to buffer and update state if + // buffer is full + if (buffer_size > 0 && buffer_size < STATE_SIZE) { + unsigned int rem_buffer_size = STATE_SIZE - buffer_size; + copy_size = clen <= rem_buffer_size ? clen : rem_buffer_size; + memcpy(ctx->aegis256.buffer + buffer_size, c, copy_size); + buffer_size += copy_size; + if (buffer_size == STATE_SIZE) { + aegis256_dec(m, ctx->aegis256.buffer, state); + buffer_size = 0; + writtenlen += STATE_SIZE; + memset(ctx->aegis256.buffer, 0, STATE_SIZE); + } + } + + // decrypt full blocks (STATE_SIZE) worth of bytes + for (i = copy_size; i + STATE_SIZE <= clen; i += STATE_SIZE) { + aegis256_dec(m + writtenlen, c + i, state); + writtenlen += STATE_SIZE; + } + + // Copy remaining bytes from c to buffer + unsigned int leftover = (clen - copy_size) & 0xf; + if (leftover) { + memcpy(ctx->aegis256.buffer, c + i, leftover); + buffer_size = leftover; + } + + if (outlen != NULL) { + *outlen = writtenlen; + } + ctx->aegis256.buffer_size = buffer_size; + return 0; +} + +static int aegis256_decrypt_final( + unsigned char* m, + unsigned long long* outlen, + unsigned long long mlen, + unsigned long long adlen, + const unsigned char* mac, + fizz_aegis_evp_ctx* ctx) { + unsigned int buffer_size = ctx->aegis256.buffer_size; + CRYPTO_ALIGN(16) unsigned char computed_mac[16]; + STATE ctx->aegis256.soft_state; + int ret; + + if (buffer_size > 0) { + CRYPTO_ALIGN(16) unsigned char src[STATE_SIZE]; + CRYPTO_ALIGN(16) unsigned char dst[STATE_SIZE]; + memset(src, 0, STATE_SIZE); + memcpy(src, ctx->aegis256.buffer, buffer_size); + aegis256_dec(dst, src, state); + memcpy(m, dst, buffer_size); + memset(dst, 0, buffer_size); + state[0] = AES_BLOCK_XOR(state[0], AES_BLOCK_LOAD(dst)); + sodium_memzero(dst, sizeof dst); + sodium_memzero(src, sizeof src); + sodium_memzero(ctx->aegis256.buffer, sizeof ctx->aegis256.buffer); + ctx->aegis256.buffer_size = 0; + } + + if (outlen != NULL) { + *outlen = buffer_size; + } + + aegis256_mac(computed_mac, adlen, mlen, state); + ret = crypto_verify_16(computed_mac, mac); + sodium_memzero(computed_mac, sizeof computed_mac); + sodium_memzero(state, sizeof state); + return ret; +} + struct crypto_aead_aegis256_implementation fizz_crypto_aead_aegis256_soft_implementation = { SODIUM_C99(.encrypt_detached =) aegis256_encrypt_detached, SODIUM_C99(.decrypt_detached =) aegis256_decrypt_detached @@ -392,4 +478,6 @@ struct aegis256_evp aegis256_soft_evp = { SODIUM_C99(.aad_final =) aegis256_aad_final, SODIUM_C99(.encrypt_update =) aegis256_encrypt_update, SODIUM_C99(.encrypt_final =) aegis256_encrypt_final, + SODIUM_C99(.decrypt_update =) aegis256_decrypt_update, + SODIUM_C99(.decrypt_final =) aegis256_decrypt_final, };