Skip to content

Commit

Permalink
Create aegis block cipher interface for aegis decrypt
Browse files Browse the repository at this point in the history
Summary:
- create aegis block cipher decrypt interface for aegis128l_soft, aegis128l_aesni, aegis256_soft, and aegis256_aesni.
- When ciphertext is chunked, we will call the `decrypt_update` on each chunk, and then call the `decrypt_final` to signal that all the chunks have been processed. Add and encrypt functionality were implemented in the previous diff.

Reviewed By: mingtaoy

Differential Revision: D47274600

fbshipit-source-id: 7cd80cbd500b1a9a1743cdd06f6d89219e643432
  • Loading branch information
Huilin Chen authored and facebook-github-bot committed Jul 12, 2023
1 parent b9af13a commit cdfe0b8
Show file tree
Hide file tree
Showing 13 changed files with 580 additions and 30 deletions.
117 changes: 88 additions & 29 deletions fizz/crypto/aead/AEGISCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,43 +97,70 @@ std::unique_ptr<folly::IOBuf> aegisEncrypt(
}

folly::Optional<std::unique_ptr<folly::IOBuf>> 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<folly::IOBuf>&& 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<folly::IOBuf> 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<int>::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<int>::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;
}
Expand All @@ -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)
Expand All @@ -156,6 +185,8 @@ AEGISCipher::AEGISCipher(
aadFinal_(addFinal),
encryptUpdate_(encryptUpdate),
encryptFinal_(encryptFinal),
decryptUpdate_(decryptUpdate),
decryptFinal_(decryptFinal),
ctx_({}),
keyLength_(keyLength),
ivLength_(ivLength),
Expand All @@ -182,6 +213,8 @@ std::unique_ptr<Aead> 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));
Expand All @@ -195,6 +228,8 @@ std::unique_ptr<Aead> 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));
Expand Down Expand Up @@ -283,13 +318,37 @@ folly::Optional<std::unique_ptr<folly::IOBuf>> 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<uint8_t, kTagLength> 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 {
Expand Down
18 changes: 18 additions & 0 deletions fizz/crypto/aead/AEGISCipher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Aead> make128L();
static std::unique_ptr<Aead> make256();
Expand Down Expand Up @@ -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);
Expand All @@ -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_;
Expand Down
24 changes: 23 additions & 1 deletion fizz/crypto/aead/test/AEGISCipherTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,29 @@ TEST_P(AEGISCipherTest, TestEncryptChunkedInput) {
}
}

// Adapted from libsodium's aegis 128l testing values
TEST_P(AEGISCipherTest, TestDecryptChunkedInput) {
std::unique_ptr<Aead> 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,
Expand Down
19 changes: 19 additions & 0 deletions fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
13 changes: 13 additions & 0 deletions fizz/third-party/libsodium-aegis/aegis128l/aead_aegis128l.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit cdfe0b8

Please sign in to comment.