1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2001,2008 Oracle.  All rights reserved.
5 *
6 * Some parts of this code originally written by Adam Stubblefield,
7 * -- astubble@rice.edu.
8 *
9 * $Id: aes_method.c,v 12.10 2008/01/08 20:58:08 bostic Exp $
10 */
11
12#include "db_config.h"
13
14#include "db_int.h"
15#include "dbinc/crypto.h"
16#include "dbinc/hmac.h"
17
18static void __aes_err __P((ENV *, int));
19static int __aes_derivekeys __P((ENV *, DB_CIPHER *, u_int8_t *, size_t));
20
21/*
22 * __aes_setup --
23 *	Setup AES functions.
24 *
25 * PUBLIC: int __aes_setup __P((ENV *, DB_CIPHER *));
26 */
27int
28__aes_setup(env, db_cipher)
29	ENV *env;
30	DB_CIPHER *db_cipher;
31{
32	AES_CIPHER *aes_cipher;
33	int ret;
34
35	db_cipher->adj_size = __aes_adj_size;
36	db_cipher->close = __aes_close;
37	db_cipher->decrypt = __aes_decrypt;
38	db_cipher->encrypt = __aes_encrypt;
39	db_cipher->init = __aes_init;
40	if ((ret = __os_calloc(env, 1, sizeof(AES_CIPHER), &aes_cipher)) != 0)
41		return (ret);
42	db_cipher->data = aes_cipher;
43	return (0);
44}
45
46/*
47 * __aes_adj_size --
48 *	Given a size, return an addition amount needed to meet the
49 *	"chunk" needs of the algorithm.
50 *
51 * PUBLIC: u_int __aes_adj_size __P((size_t));
52 */
53u_int
54__aes_adj_size(len)
55	size_t len;
56{
57	if (len % DB_AES_CHUNK == 0)
58		return (0);
59	return (DB_AES_CHUNK - (u_int)(len % DB_AES_CHUNK));
60}
61
62/*
63 * __aes_close --
64 *	Destroy the AES encryption instantiation.
65 *
66 * PUBLIC: int __aes_close __P((ENV *, void *));
67 */
68int
69__aes_close(env, data)
70	ENV *env;
71	void *data;
72{
73	__os_free(env, data);
74	return (0);
75}
76
77/*
78 * __aes_decrypt --
79 *	Decrypt data with AES.
80 *
81 * PUBLIC: int __aes_decrypt __P((ENV *, void *, void *,
82 * PUBLIC:     u_int8_t *, size_t));
83 */
84int
85__aes_decrypt(env, aes_data, iv, cipher, cipher_len)
86	ENV *env;
87	void *aes_data;
88	void *iv;
89	u_int8_t *cipher;
90	size_t cipher_len;
91{
92	AES_CIPHER *aes;
93	cipherInstance c;
94	int ret;
95
96	aes = (AES_CIPHER *)aes_data;
97	if (iv == NULL || cipher == NULL)
98		return (EINVAL);
99	if ((cipher_len % DB_AES_CHUNK) != 0)
100		return (EINVAL);
101	/*
102	 * Initialize the cipher
103	 */
104	if ((ret = __db_cipherInit(&c, MODE_CBC, iv)) < 0) {
105		__aes_err(env, ret);
106		return (EAGAIN);
107	}
108
109	/* Do the decryption */
110	if ((ret = __db_blockDecrypt(&c, &aes->decrypt_ki, cipher,
111	    cipher_len * 8, cipher)) < 0) {
112		__aes_err(env, ret);
113		return (EAGAIN);
114	}
115	return (0);
116}
117
118/*
119 * __aes_encrypt --
120 *	Encrypt data with AES.
121 *
122 * PUBLIC: int __aes_encrypt __P((ENV *, void *, void *,
123 * PUBLIC:     u_int8_t *, size_t));
124 */
125int
126__aes_encrypt(env, aes_data, iv, data, data_len)
127	ENV *env;
128	void *aes_data;
129	void *iv;
130	u_int8_t *data;
131	size_t data_len;
132{
133	AES_CIPHER *aes;
134	cipherInstance c;
135	u_int32_t tmp_iv[DB_IV_BYTES/4];
136	int ret;
137
138	aes = (AES_CIPHER *)aes_data;
139	if (aes == NULL || data == NULL)
140		return (EINVAL);
141	if ((data_len % DB_AES_CHUNK) != 0)
142		return (EINVAL);
143	/*
144	 * Generate the IV here.  We store it in a tmp IV because
145	 * the IV might be stored within the data we are encrypting
146	 * and so we will copy it over to the given location after
147	 * encryption is done.
148	 * We don't do this outside of there because some encryption
149	 * algorithms someone might add may not use IV's and we always
150	 * want on here.
151	 */
152	if ((ret = __db_generate_iv(env, tmp_iv)) != 0)
153		return (ret);
154
155	/*
156	 * Initialize the cipher
157	 */
158	if ((ret = __db_cipherInit(&c, MODE_CBC, (char *)tmp_iv)) < 0) {
159		__aes_err(env, ret);
160		return (EAGAIN);
161	}
162
163	/* Do the encryption */
164	if ((ret = __db_blockEncrypt(&c, &aes->encrypt_ki, data, data_len * 8,
165	    data)) < 0) {
166		__aes_err(env, ret);
167		return (EAGAIN);
168	}
169	memcpy(iv, tmp_iv, DB_IV_BYTES);
170	return (0);
171}
172
173/*
174 * __aes_init --
175 *	Initialize the AES encryption instantiation.
176 *
177 * PUBLIC: int __aes_init __P((ENV *, DB_CIPHER *));
178 */
179int
180__aes_init(env, db_cipher)
181	ENV *env;
182	DB_CIPHER *db_cipher;
183{
184	DB_ENV *dbenv;
185
186	dbenv = env->dbenv;
187
188	return (__aes_derivekeys(
189	    env, db_cipher, (u_int8_t *)dbenv->passwd, dbenv->passwd_len));
190}
191
192static int
193__aes_derivekeys(env, db_cipher, passwd, plen)
194	ENV *env;
195	DB_CIPHER *db_cipher;
196	u_int8_t *passwd;
197	size_t plen;
198{
199	AES_CIPHER *aes;
200	SHA1_CTX ctx;
201	int ret;
202	u_int32_t temp[DB_MAC_KEY/4];
203
204	if (passwd == NULL)
205		return (EINVAL);
206
207	aes = (AES_CIPHER *)db_cipher->data;
208
209	/* Derive the crypto keys */
210	__db_SHA1Init(&ctx);
211	__db_SHA1Update(&ctx, passwd, plen);
212	__db_SHA1Update(&ctx, (u_int8_t *)DB_ENC_MAGIC, strlen(DB_ENC_MAGIC));
213	__db_SHA1Update(&ctx, passwd, plen);
214	__db_SHA1Final((u_int8_t *)temp, &ctx);
215
216	if ((ret = __db_makeKey(&aes->encrypt_ki, DIR_ENCRYPT,
217	    DB_AES_KEYLEN, (char *)temp)) != TRUE) {
218		__aes_err(env, ret);
219		return (EAGAIN);
220	}
221	if ((ret = __db_makeKey(&aes->decrypt_ki, DIR_DECRYPT,
222	    DB_AES_KEYLEN, (char *)temp)) != TRUE) {
223		__aes_err(env, ret);
224		return (EAGAIN);
225	}
226	return (0);
227}
228
229/*
230 * __aes_err --
231 *	Handle AES-specific errors.  Codes and messages derived from
232 *	rijndael/rijndael-api-fst.h.
233 */
234static void
235__aes_err(env, err)
236	ENV *env;
237	int err;
238{
239	char *errstr;
240
241	switch (err) {
242	case BAD_KEY_DIR:
243		errstr = "AES key direction is invalid";
244		break;
245	case BAD_KEY_MAT:
246		errstr = "AES key material not of correct length";
247		break;
248	case BAD_KEY_INSTANCE:
249		errstr = "AES key passwd not valid";
250		break;
251	case BAD_CIPHER_MODE:
252		errstr = "AES cipher in wrong state (not initialized)";
253		break;
254	case BAD_BLOCK_LENGTH:
255		errstr = "AES bad block length";
256		break;
257	case BAD_CIPHER_INSTANCE:
258		errstr = "AES cipher instance is invalid";
259		break;
260	case BAD_DATA:
261		errstr = "AES data contents are invalid";
262		break;
263	case BAD_OTHER:
264		errstr = "AES unknown error";
265		break;
266	default:
267		errstr = "AES error unrecognized";
268		break;
269	}
270	__db_errx(env, errstr);
271	return;
272}
273