Skip to content

Commit 005b5cd

Browse files
authored
Make plain data encryption conditional (#6610)
1 parent 7573264 commit 005b5cd

File tree

1 file changed

+80
-16
lines changed

1 file changed

+80
-16
lines changed

src/crypto/openssl/symmetric_key.cpp

+80-16
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,57 @@ namespace ccf::crypto
5454
std::vector<uint8_t>& cipher,
5555
uint8_t tag[GCM_SIZE_TAG]) const
5656
{
57-
std::vector<uint8_t> cb(plain.size());
58-
int len = 0;
57+
if (aad.empty() && plain.empty())
58+
{
59+
throw std::logic_error("aad and plain cannot both be empty");
60+
}
61+
5962
Unique_EVP_CIPHER_CTX ctx;
6063
CHECK1(EVP_EncryptInit_ex(ctx, evp_cipher, NULL, key.data(), NULL));
64+
6165
CHECK1(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL));
6266
CHECK1(EVP_EncryptInit_ex(ctx, NULL, NULL, key.data(), iv.data()));
67+
6368
if (!aad.empty())
64-
CHECK1(EVP_EncryptUpdate(ctx, NULL, &len, aad.data(), aad.size()));
65-
CHECK1(EVP_EncryptUpdate(ctx, cb.data(), &len, plain.data(), plain.size()));
66-
CHECK1(EVP_EncryptFinal_ex(ctx, cb.data() + len, &len));
69+
{
70+
int aad_outl{0};
71+
CHECK1(EVP_EncryptUpdate(ctx, NULL, &aad_outl, aad.data(), aad.size()));
72+
73+
// As we set out buffer to NULL, we expect the output length to be 0.
74+
// However, openssl 1.1.1 sets it to the input length, which doesn't break
75+
// the encryption, but still looks wrong.
76+
#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
77+
assert(aad_outl == 0);
78+
#endif
79+
}
80+
81+
std::vector<uint8_t> ciphertext(plain.size());
82+
if (!plain.empty())
83+
{
84+
int cypher_outl{0};
85+
CHECK1(EVP_EncryptUpdate(
86+
ctx, ciphertext.data(), &cypher_outl, plain.data(), plain.size()));
87+
88+
// As we use no padding, we expect the input and output lengths to match.
89+
assert(static_cast<size_t>(cypher_outl) == plain.size());
90+
}
91+
92+
int final_outl{0};
93+
CHECK1(EVP_EncryptFinal_ex(ctx, NULL, &final_outl));
94+
95+
// As long a we use GSM cipher, the final outl must be 0, because there's no
96+
// padding and the block size is equal to 1, so EncryptUpdate() always does
97+
// the whole thing. Final is still a must to finalize and check the error.
98+
//
99+
// See https://docs.openssl.org/3.3/man3/EVP_EncryptInit/#aead-interface.
100+
assert(final_outl == 0);
101+
67102
CHECK1(
68103
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, GCM_SIZE_TAG, &tag[0]));
69104

70105
if (!plain.empty())
71106
{
72-
cipher = std::move(cb);
107+
cipher = std::move(ciphertext);
73108
}
74109
}
75110

@@ -80,28 +115,57 @@ namespace ccf::crypto
80115
std::span<const uint8_t> aad,
81116
std::vector<uint8_t>& plain) const
82117
{
83-
std::vector<uint8_t> pb(cipher.size());
84-
85-
int len = 0;
86118
Unique_EVP_CIPHER_CTX ctx;
87119
CHECK1(EVP_DecryptInit_ex(ctx, evp_cipher, NULL, NULL, NULL));
88120
CHECK1(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL));
121+
89122
CHECK1(EVP_DecryptInit_ex(ctx, NULL, NULL, key.data(), iv.data()));
90123
if (!aad.empty())
91-
CHECK1(EVP_DecryptUpdate(ctx, NULL, &len, aad.data(), aad.size()));
92-
CHECK1(
93-
EVP_DecryptUpdate(ctx, pb.data(), &len, cipher.data(), cipher.size()));
124+
{
125+
int aad_outl{0};
126+
CHECK1(EVP_DecryptUpdate(ctx, NULL, &aad_outl, aad.data(), aad.size()));
127+
128+
// As we set out buffer to NULL, we expect the output length to be 0.
129+
// However, openssl 1.1.1 sets it to the input length, which doesn't break
130+
// the encryption, but still looks wrong.
131+
#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
132+
assert(aad_outl == 0);
133+
#endif
134+
}
135+
136+
std::vector<uint8_t> plaintext(cipher.size());
137+
if (!cipher.empty())
138+
{
139+
int plain_outl{0};
140+
CHECK1(EVP_DecryptUpdate(
141+
ctx, plaintext.data(), &plain_outl, cipher.data(), cipher.size()));
142+
143+
// As we use no padding, we expect the input and output lengths to match.
144+
assert(plain_outl == cipher.size());
145+
}
146+
94147
CHECK1(EVP_CIPHER_CTX_ctrl(
95148
ctx, EVP_CTRL_GCM_SET_TAG, GCM_SIZE_TAG, (uint8_t*)tag));
96149

97-
int r = EVP_DecryptFinal_ex(ctx, pb.data() + len, &len) > 0;
150+
int final_outl{0};
151+
if (EVP_DecryptFinal_ex(ctx, NULL, &final_outl) != 1)
152+
{
153+
return false;
154+
}
155+
156+
// As long a we use GSM cipher, the final outl must be 0, because there's no
157+
// padding and the block size is equal to 1, so EncryptUpdate() always does
158+
// the whole thing. Final is still a must to finalize and check the error.
159+
//
160+
// See https://docs.openssl.org/3.3/man3/EVP_EncryptInit/#aead-interface.
161+
assert(final_outl == 0);
98162

99-
if (r == 1 && !cipher.empty())
163+
if (!cipher.empty())
100164
{
101-
plain = std::move(pb);
165+
plain = std::move(plaintext);
102166
}
103167

104-
return r == 1;
168+
return true;
105169
}
106170

107171
std::vector<uint8_t> KeyAesGcm_OpenSSL::ckm_aes_key_wrap_pad(

0 commit comments

Comments
 (0)