forked from Tarsnap/scrypt
-
Notifications
You must be signed in to change notification settings - Fork 19
/
crypto_aes.c
173 lines (140 loc) · 3.76 KB
/
crypto_aes.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include "cpusupport.h"
#include "crypto_aes_aesni.h"
#include "insecure_memzero.h"
#include "warnp.h"
#include "crypto_aes.h"
/**
* This represents either an AES_KEY or a struct crypto_aes_key_aesni; we
* know which it is based on whether we're using AESNI code or not. As such,
* it's just an opaque pointer; but declaring it as a named structure type
* prevents type-mismatch bugs in upstream code.
*/
struct crypto_aes_key;
#ifdef CPUSUPPORT_X86_AESNI
/* Test whether OpenSSL and AESNI code produce the same AES ciphertext. */
static int
aesnitest(uint8_t ptext[16], uint8_t * key, size_t len)
{
AES_KEY kexp_openssl;
void * kexp_aesni;
uint8_t ctext_openssl[16];
uint8_t ctext_aesni[16];
/* Sanity-check. */
assert((len == 16) || (len == 32));
/* Expand the key. */
AES_set_encrypt_key(key, (int)(len * 8), &kexp_openssl);
if ((kexp_aesni = crypto_aes_key_expand_aesni(key, len)) == NULL)
goto err0;
/* Encrypt the block. */
AES_encrypt(ptext, ctext_openssl, &kexp_openssl);
crypto_aes_encrypt_block_aesni(ptext, ctext_aesni, kexp_aesni);
/* Free the AESNI expanded key. */
crypto_aes_key_free_aesni(kexp_aesni);
/* Do the outputs match? */
return (memcmp(ctext_openssl, ctext_aesni, 16));
err0:
/* Failure! */
return (-1);
}
/* Should we use AESNI? */
static int
useaesni(void)
{
static int aesnigood = -1;
uint8_t key[32];
uint8_t ptext[16];
size_t i;
/* If we haven't decided which code to use yet, decide now. */
while (aesnigood == -1) {
/* Default to OpenSSL. */
aesnigood = 0;
/* If the CPU doesn't claim to support AESNI, stop here. */
if (!cpusupport_x86_aesni())
break;
/* Test cases: key is 0x00010203..., ptext is 0x00112233... */
for (i = 0; i < 16; i++)
ptext[i] = (0x11 * i) & 0xff;
for (i = 0; i < 32; i++)
key[i] = i & 0xff;
/* Test that AESNI and OpenSSL produce the same results. */
if (aesnitest(ptext, key, 16) || aesnitest(ptext, key, 32)) {
warn0("Disabling AESNI due to failed self-test");
break;
}
/* AESNI works; use it. */
aesnigood = 1;
}
return (aesnigood);
}
#endif /* CPUSUPPORT_X86_AESNI */
/**
* crypto_aes_key_expand(key, len):
* Expand the ${len}-byte AES key ${key} into a structure which can be passed
* to crypto_aes_encrypt_block. The length must be 16 or 32.
*/
struct crypto_aes_key *
crypto_aes_key_expand(const uint8_t * key, size_t len)
{
AES_KEY * kexp;
/* Sanity-check. */
assert((len == 16) || (len == 32));
#ifdef CPUSUPPORT_X86_AESNI
/* Use AESNI if we can. */
if (useaesni())
return (crypto_aes_key_expand_aesni(key, len));
#endif
/* Allocate structure. */
if ((kexp = malloc(sizeof(AES_KEY))) == NULL)
goto err0;
/* Expand the key. */
AES_set_encrypt_key(key, (int)(len * 8), kexp);
/* Success! */
return ((void *)kexp);
err0:
/* Failure! */
return (NULL);
}
/**
* crypto_aes_encrypt_block(in, out, key):
* Using the expanded AES key ${key}, encrypt the block ${in} and write the
* resulting ciphertext to ${out}.
*/
void
crypto_aes_encrypt_block(const uint8_t * in, uint8_t * out,
const struct crypto_aes_key * key)
{
#ifdef CPUSUPPORT_X86_AESNI
if (useaesni()) {
crypto_aes_encrypt_block_aesni(in, out, (const void *)key);
return;
}
#endif
/* Get AES to do the work. */
AES_encrypt(in, out, (const void *)key);
}
/**
* crypto_aes_key_free(key):
* Free the expanded AES key ${key}.
*/
void
crypto_aes_key_free(struct crypto_aes_key * key)
{
#ifdef CPUSUPPORT_X86_AESNI
if (useaesni()) {
crypto_aes_key_free_aesni((void *)key);
return;
}
#endif
/* Behave consistently with free(NULL). */
if (key == NULL)
return;
/* Attempt to zero the expanded key. */
insecure_memzero(key, sizeof(AES_KEY));
/* Free the key. */
free(key);
}