Skip to content

Commit

Permalink
Create aegis block cipher interface for aegis encrypt
Browse files Browse the repository at this point in the history
Summary:
- Create aegis block cipher encrypt interface for aegis128l_soft, aegis128l_aesni, aegis256_soft, and aegis256_aesni.
- The new encrypt interface is consisted of following functions. When aad or input is chunked, we will call the update function on each chunk, and then call the final function to signal that all the chunks have been processed.
  - init_state
    - initiate state values
  - aad_update/encrypt_update
    -  If buffer has existing bytes, copy to buffer and process the bytes if buffer is full
    - process [16/32] bytes at a time to state
    - copy remaining bytes from to buffer
  - aad_final/encrypt_final
    - if buffer has bytes, process those bytes

- Added `TestEncryptChunkedAad` and `TestEncryptChunkedInput` to test the new interface

Reviewed By: mingtaoy

Differential Revision: D46780195

fbshipit-source-id: 3bc3b85c12a2e8eda54353a5eb77aec502f7fc70
  • Loading branch information
Huilin Chen authored and facebook-github-bot committed Jul 6, 2023
1 parent f16e2e8 commit 647943f
Show file tree
Hide file tree
Showing 18 changed files with 1,001 additions and 39 deletions.
109 changes: 77 additions & 32 deletions fizz/crypto/aead/AEGISCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#if FIZZ_BUILD_AEGIS

#include <fizz/crypto/aead/CryptoUtil.h>
#include <fizz/third-party/libsodium-aegis/aegis.h>
#include <folly/lang/CheckedMath.h>
#include <functional>

Expand All @@ -24,50 +23,74 @@ static_assert(
namespace {

std::unique_ptr<folly::IOBuf> aegisEncrypt(
AEGISCipher::EncryptFn encrypt,
AEGISCipher::InitStateFn initstate,
AEGISCipher::AadUpdateFn aadUpdate,
AEGISCipher::AadFinalFn aadFinal,
AEGISCipher::EncryptUpdateFn encryptUpdate,
AEGISCipher::EncryptFinalFn encryptFinal,
AEGISCipher::AegisEVPCtx ctx,
std::unique_ptr<folly::IOBuf>&& plaintext,
const folly::IOBuf* associatedData,
folly::ByteRange iv,
folly::ByteRange key,
size_t tagLen,
size_t headroom) {
folly::ByteRange input = plaintext->coalesce();
if (initstate(key.data(), iv.data(), &ctx) != 0) {
throw std::runtime_error("Initiate encryption state error");
}

auto inputLength = plaintext->computeChainDataLength();
std::unique_ptr<folly::IOBuf> output;
folly::IOBuf* input;
// create enough to also fit the tag and headroom
size_t totalSize{0};
if (!folly::checked_add<size_t>(&totalSize, headroom, input.size(), tagLen)) {
if (!folly::checked_add<size_t>(&totalSize, headroom, inputLength, tagLen)) {
throw std::overflow_error("Output buffer size");
}
output = folly::IOBuf::create(totalSize);
output->advance(headroom);
output->append(input.size() + tagLen);
output->append(inputLength + tagLen);
input = plaintext.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<int>::max()) {
throw std::runtime_error("too much associated data");
}
if (aadUpdate(current.data(), current.size(), &ctx) != 0) {
throw std::runtime_error("Encryption aad update error");
}
adlen += current.size();
}
if (aadFinal(&ctx) != 0) {
throw std::runtime_error("Encryption aad final error");
}
ad = associatedData->data();
adlen = associatedData->length();
} else {
ad = nullptr;
adlen = 0;
}

unsigned long long ciphertextLength;
int ret = encrypt(
output->writableData(),
&ciphertextLength,
input.data(),
input.size(),
ad,
adlen,
nullptr,
iv.data(),
key.data());
if (ret != 0 || ciphertextLength != (input.size() + tagLen)) {
unsigned long long writtenlen = 0;
unsigned long long totalWritten = 0;
for (auto current : *input) {
if (current.size() > std::numeric_limits<int>::max()) {
throw std::runtime_error("too much plaintext data");
}
if (encryptUpdate(
output->writableData() + totalWritten,
&writtenlen,
current.data(),
current.size(),
&ctx) != 0) {
throw std::runtime_error("Encryption update error");
}
totalWritten += writtenlen;
}
if (encryptFinal(
output->writableData() + totalWritten,
&writtenlen,
inputLength,
adlen,
&ctx) != 0 ||
totalWritten + writtenlen != (inputLength + tagLen)) {
throw std::runtime_error("Encryption error");
}
return output;
Expand Down Expand Up @@ -118,13 +141,22 @@ folly::Optional<std::unique_ptr<folly::IOBuf>> aegisDecrypt(
} // namespace

AEGISCipher::AEGISCipher(
EncryptFn encrypt,
DecryptFn decrypt,
InitStateFn init,
AadUpdateFn aadUpdate,
AadFinalFn addFinal,
EncryptUpdateFn encryptUpdate,
EncryptFinalFn encryptFinal,
size_t keyLength,
size_t ivLength,
size_t tagLength)
: encrypt_(encrypt),
decrypt_(decrypt),
: decrypt_(decrypt),
initstate_(init),
aadUpdate_(aadUpdate),
aadFinal_(addFinal),
encryptUpdate_(encryptUpdate),
encryptFinal_(encryptFinal),
ctx_({}),
keyLength_(keyLength),
ivLength_(ivLength),
tagLength_(tagLength) {
Expand All @@ -144,17 +176,25 @@ AEGISCipher::AEGISCipher(

std::unique_ptr<Aead> AEGISCipher::make128L() {
return std::unique_ptr<Aead>(new AEGISCipher(
fizz_aegis128l_encrypt,
fizz_aegis128l_decrypt,
aegis128l_init_state,
aegis128l_aad_update,
aegis128l_aad_final,
aegis128l_encrypt_update,
aegis128l_encrypt_final,
fizz_aegis128l_KEYBYTES,
fizz_aegis128l_NPUBBYTES,
fizz_aegis128l_ABYTES));
}

std::unique_ptr<Aead> AEGISCipher::make256() {
return std::unique_ptr<Aead>(new AEGISCipher(
fizz_aegis256_encrypt,
fizz_aegis256_decrypt,
aegis256_init_state,
aegis256_aad_update,
aegis256_aad_final,
aegis256_encrypt_update,
aegis256_encrypt_final,
fizz_aegis256_KEYBYTES,
fizz_aegis256_NPUBBYTES,
fizz_aegis256_ABYTES));
Expand Down Expand Up @@ -202,7 +242,12 @@ std::unique_ptr<folly::IOBuf> AEGISCipher::encrypt(
folly::ByteRange nonce,
Aead::AeadOptions /*options*/) const {
return aegisEncrypt(
encrypt_,
initstate_,
aadUpdate_,
aadFinal_,
encryptUpdate_,
encryptFinal_,
ctx_,
std::move(plaintext),
associatedData,
nonce,
Expand Down
36 changes: 34 additions & 2 deletions fizz/crypto/aead/AEGISCipher.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <fizz/crypto/aead/Aead.h>
#include <fizz/crypto/aead/IOBufUtil.h>
#include <fizz/third-party/libsodium-aegis/aegis.h>
#include <folly/Conv.h>
#include <folly/Memory.h>
#include <folly/Range.h>
Expand All @@ -23,6 +24,7 @@
namespace fizz {
class AEGISCipher : public Aead {
public:
using AegisEVPCtx = fizz_aegis_evp_ctx;
using EncryptFn = int (*const)(
unsigned char* c,
unsigned long long* clen_p,
Expand All @@ -43,6 +45,27 @@ class AEGISCipher : public Aead {
unsigned long long adlen,
const unsigned char* npub,
const unsigned char* k);
using InitStateFn = int (*const)(
const unsigned char* key,
const unsigned char* nonce,
AegisEVPCtx* ctx);
using AadUpdateFn = int (*const)(
const unsigned char* ad,
unsigned long long adlen,
AegisEVPCtx* ctx);
using AadFinalFn = int (*const)(AegisEVPCtx* ctx);
using EncryptUpdateFn = int (*const)(
unsigned char* c,
unsigned long long* clen_p,
const unsigned char* m,
unsigned long long mlen,
AegisEVPCtx* ctx);
using EncryptFinalFn = int (*const)(
unsigned char* c,
unsigned long long* c_writtenlen_p,
unsigned long long mlen,
unsigned long long adlen,
AegisEVPCtx* ctx);

static constexpr size_t kMaxIVLength = 32;

Expand Down Expand Up @@ -98,8 +121,12 @@ class AEGISCipher : public Aead {

private:
AEGISCipher(
EncryptFn encrypt,
DecryptFn decrypt,
InitStateFn init,
AadUpdateFn aadUpdate,
AadFinalFn aadFinal_,
EncryptUpdateFn encryptUpdate,
EncryptFinalFn encryptFinal,
size_t keyLength,
size_t ivLength,
size_t tagLength);
Expand All @@ -110,8 +137,13 @@ class AEGISCipher : public Aead {
size_t headroom_{0};

// set by the ctor
EncryptFn encrypt_;
DecryptFn decrypt_;
InitStateFn initstate_;
AadUpdateFn aadUpdate_;
AadFinalFn aadFinal_;
EncryptUpdateFn encryptUpdate_;
EncryptFinalFn encryptFinal_;
AegisEVPCtx ctx_;
size_t keyLength_;
size_t ivLength_;
size_t tagLength_;
Expand Down
54 changes: 51 additions & 3 deletions fizz/crypto/aead/test/AEGISCipherTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,46 @@ TEST_P(AEGISCipherTest, TestDecrypt) {
}
}

TEST_P(AEGISCipherTest, TestEncryptChunkedAad) {
std::unique_ptr<Aead> cipher = getTestCipher(GetParam());
auto plaintext = toIOBuf(GetParam().plaintext);
auto origLength = plaintext->computeChainDataLength();
auto nonce_iobuf = toIOBuf(GetParam().iv);
auto nonce = folly::ByteRange(nonce_iobuf->data(), nonce_iobuf->length());
auto aadLength = toIOBuf(GetParam().aad)->computeChainDataLength();
for (size_t i = 2; i < aadLength; i++) {
auto aad = toIOBuf(GetParam().aad);
auto chunkedAad = chunkIOBuf(std::move(aad), i);
auto out = cipher->encrypt(std::move(plaintext), chunkedAad.get(), nonce);
bool valid = IOBufEqualTo()(toIOBuf(GetParam().ciphertext), out);
EXPECT_EQ(valid, GetParam().valid);
EXPECT_EQ(
out->computeChainDataLength(),
origLength + cipher->getCipherOverhead());
}
}

TEST_P(AEGISCipherTest, TestEncryptChunkedInput) {
std::unique_ptr<Aead> cipher = getTestCipher(GetParam());
auto origLength = toIOBuf(GetParam().plaintext)->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 plaintext = toIOBuf(GetParam().plaintext);
auto chunkedInput = chunkIOBuf(std::move(plaintext), i);
auto out = cipher->encrypt(std::move(chunkedInput), aad.get(), nonce);
bool valid = IOBufEqualTo()(toIOBuf(GetParam().ciphertext), out);
EXPECT_EQ(valid, GetParam().valid);
EXPECT_EQ(
out->computeChainDataLength(),
origLength + cipher->getCipherOverhead());
}
}

// Adapted from libsodium's aegis 128l testing values
INSTANTIATE_TEST_SUITE_P(
AEGIS128TestVectors,
AEGIS128LTestVectors,
AEGISCipherTest,
::testing::Values(
AEGISCipherParams{
Expand Down Expand Up @@ -125,7 +162,11 @@ INSTANTIATE_TEST_SUITE_P(
"dc5180954df0c3391a60b44cbf70aee72b7dbb2addc90a0bf2ceac6113287eb501fe1ea9f4c51822664b82fe0279b039f4",
"c8a7d9131cebfa5388003cc30deac523aa9b09d148affff06ba40400e09ca900db770e07cedf5cd0647f6723c810ffcb596cac51edd6f49cd7be0010a3ac29e704",
false,
CipherSuite::TLS_AEGIS_128L_SHA256_EXPERIMENTAL},
CipherSuite::TLS_AEGIS_128L_SHA256_EXPERIMENTAL}));
INSTANTIATE_TEST_SUITE_P(
AEGIS256TestVectors,
AEGISCipherTest,
::testing::Values(
AEGISCipherParams{
"7083505997f52fdf86548d86ee87c1429ed91f108cd56384dc840269ef7fdd73",
"18cd778e6f5b1d35d4ca975fd719a17aaf22c3eba01928b6a78bac5810c92c75",
Expand All @@ -142,7 +183,14 @@ INSTANTIATE_TEST_SUITE_P(
"b8565db06c2fa493e09b6764f4d09296422095eb6e9890f606654713bfee6f362a123688b61f254f315f18b20bcc5ed8b0b4f2224de9f498e3ef03532a8bcddb361f5ace8ff491bab8b3d06550496501264f9f48ebad277e7492146789d0fc1a3b1e3e81598370a4183683d1fee25a9a1fe359c836932746b983d01767ad4b9b3d70cc917fe57e41e0",
true,
CipherSuite::TLS_AEGIS_256_SHA384_EXPERIMENTAL},
AEGISCipherParams{"77b473865175ebd5ddf9c382bac227029c25bdb836e683a138e4618cc964488b", "f183d8de1e6dd4ccefa79fe22fabfda58e68dd29116d13408042f0713a4ee5f8", "0679fd74a846965e33e558676115d843e440fa37092fbd5c57c82fd914210fcf948f911b04632d66be46248d772b3eb9f55b537e54b1ec751b63f035c8", "9888b8ee03c3217a777b7558a31e331909570ea196f02c8cffad2c8dc6499b8125363c06a71c057842666bfb5c6acc937d2eecd960330c2361abdd88a4b191557ddf5102de75ddc7e09aee9862f32e24f1db3847a5f5b379fb32e2ef7ffb0d3a60", "3464d835302583ade6ed99e23333e865d3308f31a6cb65bcefdc9a1b9b4d0e0f75513188480dac4a64922af4441324ce7de74eb9f7f4e414f6177a4814edc96313694b99ff8dd36b2f7f79c7ecd70ec475abe1c1909238767f172fd6b95e92c025b1f8c9704d7b845964e14ccb333f0d4b", true, CipherSuite::TLS_AEGIS_256_SHA384_EXPERIMENTAL},
AEGISCipherParams{
"77b473865175ebd5ddf9c382bac227029c25bdb836e683a138e4618cc964488b",
"f183d8de1e6dd4ccefa79fe22fabfda58e68dd29116d13408042f0713a4ee5f8",
"0679fd74a846965e33e558676115d843e440fa37092fbd5c57c82fd914210fcf948f911b04632d66be46248d772b3eb9f55b537e54b1ec751b63f035c8",
"9888b8ee03c3217a777b7558a31e331909570ea196f02c8cffad2c8dc6499b8125363c06a71c057842666bfb5c6acc937d2eecd960330c2361abdd88a4b191557ddf5102de75ddc7e09aee9862f32e24f1db3847a5f5b379fb32e2ef7ffb0d3a60",
"3464d835302583ade6ed99e23333e865d3308f31a6cb65bcefdc9a1b9b4d0e0f75513188480dac4a64922af4441324ce7de74eb9f7f4e414f6177a4814edc96313694b99ff8dd36b2f7f79c7ecd70ec475abe1c1909238767f172fd6b95e92c025b1f8c9704d7b845964e14ccb333f0d4b",
true,
CipherSuite::TLS_AEGIS_256_SHA384_EXPERIMENTAL},
AEGISCipherParams{
"b8c6e8cea59ca9fd2922530ee61911c1ed1c5af98be8fb03cbb449adcea0ed83",
"af5bc1abe7bafadee790390277874cdfcc1ac1955f249d1131555d345832f555",
Expand Down
42 changes: 40 additions & 2 deletions fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#include "crypto_aead_aegis128l.h"
#include <sodium.h>

#include "aead_aegis128l.h"

#include "soft/aead_aegis128l_soft.h"

#include <fizz/third-party/libsodium-aegis/private/config.h>
Expand All @@ -17,6 +15,8 @@

static const crypto_aead_aegis128l_implementation *implementation =
&fizz_crypto_aead_aegis128l_soft_implementation;
static const aegis128l_evp* aegis_evp =
&aegis128l_soft_evp;

size_t
fizz_aegis128l_keybytes(void)
Expand Down Expand Up @@ -119,14 +119,52 @@ fizz_aegis128l_decrypt_detached(unsigned char *m, unsigned char *nsec,
return implementation->decrypt_detached(m, nsec, c, clen, mac, ad, adlen, npub, k);
}

int aegis128l_init_state(
const unsigned char* key,
const unsigned char* nonce,
fizz_aegis_evp_ctx* ctx) {
return aegis_evp->init_state(key, nonce, ctx);
}

int aegis128l_aad_update(
const unsigned char* ad,
unsigned long long adlen,
fizz_aegis_evp_ctx* ctx) {
return aegis_evp->aad_update(ad, adlen, ctx);
}

int aegis128l_aad_final(fizz_aegis_evp_ctx* ctx) {
return aegis_evp->aad_final(ctx);
}

int aegis128l_encrypt_update(
unsigned char* c,
unsigned long long* c_writtenlen_p,
const unsigned char* m,
unsigned long long mlen,
fizz_aegis_evp_ctx* ctx) {
return aegis_evp->encrypt_update(c, c_writtenlen_p, m, mlen, ctx);
}

int aegis128l_encrypt_final(
unsigned char* c,
unsigned long long* c_writtenlen_p,
unsigned long long mlen,
unsigned long long adlen,
fizz_aegis_evp_ctx* ctx) {
return aegis_evp->encrypt_final(c, c_writtenlen_p, mlen, adlen, ctx);
}

int
fizz_aegis128l_pick_best_implementation(void)
{
implementation = &fizz_crypto_aead_aegis128l_soft_implementation;
aegis_evp = &aegis128l_soft_evp;

#if FIZZ_LIBSODIUM_HAS_AESNI
if (sodium_runtime_has_aesni()) {
implementation = &fizz_crypto_aead_aegis128l_aesni_implementation;
aegis_evp = &aegis128l_aesni_evp;
return 0;
}
#endif
Expand Down
Loading

0 comments on commit 647943f

Please sign in to comment.