Skip to content

Commit 27a212d

Browse files
theunisipa
authored andcommitted
crypto: add AES 128/256 CBC classes
The output should always match openssl's, even for failed operations. Even for a decrypt with broken padding, the output is always deterministic (and attemtps to be constant-time).
1 parent 6bec172 commit 27a212d

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

src/crypto/aes.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,147 @@ void AES256Decrypt::Decrypt(unsigned char plaintext[16], const unsigned char cip
7171
{
7272
AES256_decrypt(&ctx, 1, plaintext, ciphertext);
7373
}
74+
75+
76+
template <typename T>
77+
static int CBCEncrypt(const T& enc, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out)
78+
{
79+
int written = 0;
80+
int padsize = size % AES_BLOCKSIZE;
81+
unsigned char mixed[AES_BLOCKSIZE];
82+
83+
if (!data || !size || !out)
84+
return 0;
85+
86+
if (!pad && padsize != 0)
87+
return 0;
88+
89+
memcpy(mixed, iv, AES_BLOCKSIZE);
90+
91+
// Write all but the last block
92+
while (written + AES_BLOCKSIZE <= size) {
93+
for (int i = 0; i != AES_BLOCKSIZE; i++)
94+
mixed[i] ^= *data++;
95+
enc.Encrypt(out + written, mixed);
96+
memcpy(mixed, out + written, AES_BLOCKSIZE);
97+
written += AES_BLOCKSIZE;
98+
}
99+
if (pad) {
100+
// For all that remains, pad each byte with the value of the remaining
101+
// space. If there is none, pad by a full block.
102+
for (int i = 0; i != padsize; i++)
103+
mixed[i] ^= *data++;
104+
for (int i = padsize; i != AES_BLOCKSIZE; i++)
105+
mixed[i] ^= AES_BLOCKSIZE - padsize;
106+
enc.Encrypt(out + written, mixed);
107+
written += AES_BLOCKSIZE;
108+
}
109+
return written;
110+
}
111+
112+
template <typename T>
113+
static int CBCDecrypt(const T& dec, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out)
114+
{
115+
unsigned char padsize = 0;
116+
int written = 0;
117+
bool fail = false;
118+
const unsigned char* prev = iv;
119+
120+
if (!data || !size || !out)
121+
return 0;
122+
123+
if (size % AES_BLOCKSIZE != 0)
124+
return 0;
125+
126+
// Decrypt all data. Padding will be checked in the output.
127+
while (written != size) {
128+
dec.Decrypt(out, data + written);
129+
for (int i = 0; i != AES_BLOCKSIZE; i++)
130+
*out++ ^= prev[i];
131+
prev = data + written;
132+
written += AES_BLOCKSIZE;
133+
}
134+
135+
// When decrypting padding, attempt to run in constant-time
136+
if (pad) {
137+
// If used, padding size is the value of the last decrypted byte. For
138+
// it to be valid, It must be between 1 and AES_BLOCKSIZE.
139+
padsize = *--out;
140+
fail = !padsize | (padsize > AES_BLOCKSIZE);
141+
142+
// If not well-formed, treat it as though there's no padding.
143+
padsize *= !fail;
144+
145+
// All padding must equal the last byte otherwise it's not well-formed
146+
for (int i = AES_BLOCKSIZE; i != 0; i--)
147+
fail |= ((i > AES_BLOCKSIZE - padsize) & (*out-- != padsize));
148+
149+
written -= padsize;
150+
}
151+
return written * !fail;
152+
}
153+
154+
AES256CBCEncrypt::AES256CBCEncrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
155+
: enc(key), pad(padIn)
156+
{
157+
memcpy(iv, ivIn, AES_BLOCKSIZE);
158+
}
159+
160+
int AES256CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const
161+
{
162+
return CBCEncrypt(enc, iv, data, size, pad, out);
163+
}
164+
165+
AES256CBCEncrypt::~AES256CBCEncrypt()
166+
{
167+
memset(iv, 0, sizeof(iv));
168+
}
169+
170+
AES256CBCDecrypt::AES256CBCDecrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
171+
: dec(key), pad(padIn)
172+
{
173+
memcpy(iv, ivIn, AES_BLOCKSIZE);
174+
}
175+
176+
177+
int AES256CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const
178+
{
179+
return CBCDecrypt(dec, iv, data, size, pad, out);
180+
}
181+
182+
AES256CBCDecrypt::~AES256CBCDecrypt()
183+
{
184+
memset(iv, 0, sizeof(iv));
185+
}
186+
187+
AES128CBCEncrypt::AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
188+
: enc(key), pad(padIn)
189+
{
190+
memcpy(iv, ivIn, AES_BLOCKSIZE);
191+
}
192+
193+
AES128CBCEncrypt::~AES128CBCEncrypt()
194+
{
195+
memset(iv, 0, AES_BLOCKSIZE);
196+
}
197+
198+
int AES128CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const
199+
{
200+
return CBCEncrypt(enc, iv, data, size, pad, out);
201+
}
202+
203+
AES128CBCDecrypt::AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
204+
: dec(key), pad(padIn)
205+
{
206+
memcpy(iv, ivIn, AES_BLOCKSIZE);
207+
}
208+
209+
AES128CBCDecrypt::~AES128CBCDecrypt()
210+
{
211+
memset(iv, 0, AES_BLOCKSIZE);
212+
}
213+
214+
int AES128CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const
215+
{
216+
return CBCDecrypt(dec, iv, data, size, pad, out);
217+
}

src/crypto/aes.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,56 @@ class AES256Decrypt
6363
void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const;
6464
};
6565

66+
class AES256CBCEncrypt
67+
{
68+
public:
69+
AES256CBCEncrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
70+
~AES256CBCEncrypt();
71+
int Encrypt(const unsigned char* data, int size, unsigned char* out) const;
72+
73+
private:
74+
const AES256Encrypt enc;
75+
const bool pad;
76+
unsigned char iv[AES_BLOCKSIZE];
77+
};
78+
79+
class AES256CBCDecrypt
80+
{
81+
public:
82+
AES256CBCDecrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
83+
~AES256CBCDecrypt();
84+
int Decrypt(const unsigned char* data, int size, unsigned char* out) const;
85+
86+
private:
87+
const AES256Decrypt dec;
88+
const bool pad;
89+
unsigned char iv[AES_BLOCKSIZE];
90+
};
91+
92+
class AES128CBCEncrypt
93+
{
94+
public:
95+
AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
96+
~AES128CBCEncrypt();
97+
int Encrypt(const unsigned char* data, int size, unsigned char* out) const;
98+
99+
private:
100+
const AES128Encrypt enc;
101+
const bool pad;
102+
unsigned char iv[AES_BLOCKSIZE];
103+
};
104+
105+
class AES128CBCDecrypt
106+
{
107+
public:
108+
AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
109+
~AES128CBCDecrypt();
110+
int Decrypt(const unsigned char* data, int size, unsigned char* out) const;
111+
112+
private:
113+
const AES128Decrypt dec;
114+
const bool pad;
115+
unsigned char iv[AES_BLOCKSIZE];
116+
};
117+
66118
#endif // BITCOIN_CRYPTO_AES_H

0 commit comments

Comments
 (0)