@@ -54,22 +54,57 @@ namespace ccf::crypto
54
54
std::vector<uint8_t >& cipher,
55
55
uint8_t tag[GCM_SIZE_TAG]) const
56
56
{
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
+
59
62
Unique_EVP_CIPHER_CTX ctx;
60
63
CHECK1 (EVP_EncryptInit_ex (ctx, evp_cipher, NULL , key.data (), NULL ));
64
+
61
65
CHECK1 (EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size (), NULL ));
62
66
CHECK1 (EVP_EncryptInit_ex (ctx, NULL , NULL , key.data (), iv.data ()));
67
+
63
68
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
+
67
102
CHECK1 (
68
103
EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_GET_TAG, GCM_SIZE_TAG, &tag[0 ]));
69
104
70
105
if (!plain.empty ())
71
106
{
72
- cipher = std::move (cb );
107
+ cipher = std::move (ciphertext );
73
108
}
74
109
}
75
110
@@ -80,28 +115,57 @@ namespace ccf::crypto
80
115
std::span<const uint8_t > aad,
81
116
std::vector<uint8_t >& plain) const
82
117
{
83
- std::vector<uint8_t > pb (cipher.size ());
84
-
85
- int len = 0 ;
86
118
Unique_EVP_CIPHER_CTX ctx;
87
119
CHECK1 (EVP_DecryptInit_ex (ctx, evp_cipher, NULL , NULL , NULL ));
88
120
CHECK1 (EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size (), NULL ));
121
+
89
122
CHECK1 (EVP_DecryptInit_ex (ctx, NULL , NULL , key.data (), iv.data ()));
90
123
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
+
94
147
CHECK1 (EVP_CIPHER_CTX_ctrl (
95
148
ctx, EVP_CTRL_GCM_SET_TAG, GCM_SIZE_TAG, (uint8_t *)tag));
96
149
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 );
98
162
99
- if (r == 1 && !cipher.empty ())
163
+ if (!cipher.empty ())
100
164
{
101
- plain = std::move (pb );
165
+ plain = std::move (plaintext );
102
166
}
103
167
104
- return r == 1 ;
168
+ return true ;
105
169
}
106
170
107
171
std::vector<uint8_t > KeyAesGcm_OpenSSL::ckm_aes_key_wrap_pad (
0 commit comments