1 /*
2 * Copyright (c) 2010-2012 United States Government, as represented by
3 * the Secretary of Defense. All rights reserved.
4 *
5 * THIS SOFTWARE AND ITS DOCUMENTATION ARE PROVIDED AS IS AND WITHOUT
6 * ANY EXPRESS OR IMPLIED WARRANTIES WHATSOEVER. ALL WARRANTIES
7 * INCLUDING, BUT NOT LIMITED TO, PERFORMANCE, MERCHANTABILITY, FITNESS
8 * FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT ARE HEREBY
9 * DISCLAIMED. USERS ASSUME THE ENTIRE RISK AND LIABILITY OF USING THE
10 * SOFTWARE.
11 */
12
13 #include <mini-os/byteorder.h>
14 #include "vtpmblk.h"
15 #include "tpm/tpm_marshalling.h"
16 #include "vtpm_cmd.h"
17 #include "polarssl/aes.h"
18 #include "polarssl/sha1.h"
19 #include <blkfront.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24
25 /*Encryption key and block sizes */
26 #define BLKSZ 16
27
28 static struct blkfront_dev* blkdev = NULL;
29 static int blkfront_fd = -1;
30 static uint64_t slot_size = 0;
31
init_vtpmblk(struct tpmfront_dev * tpmfront_dev)32 int init_vtpmblk(struct tpmfront_dev* tpmfront_dev)
33 {
34 struct blkfront_info blkinfo;
35 info("Initializing persistent NVM storage\n");
36
37 if((blkdev = init_blkfront(NULL, &blkinfo)) == NULL) {
38 error("BLKIO: ERROR Unable to initialize blkfront");
39 return -1;
40 }
41 if (blkinfo.info & VDISK_READONLY || blkinfo.mode != O_RDWR) {
42 error("BLKIO: ERROR block device is read only!");
43 goto error;
44 }
45 if((blkfront_fd = blkfront_open(blkdev)) == -1) {
46 error("Unable to open blkfront file descriptor!");
47 goto error;
48 }
49
50 slot_size = blkinfo.sectors * blkinfo.sector_size / 2;
51
52 return 0;
53 error:
54 shutdown_blkfront(blkdev);
55 blkdev = NULL;
56 return -1;
57 }
58
shutdown_vtpmblk(void)59 void shutdown_vtpmblk(void)
60 {
61 close(blkfront_fd);
62 blkfront_fd = -1;
63 blkdev = NULL;
64 }
65
write_vtpmblk_raw(uint8_t * data,size_t data_length,int slot)66 static int write_vtpmblk_raw(uint8_t *data, size_t data_length, int slot)
67 {
68 int rc;
69 uint32_t lenbuf;
70 debug("Begin Write data=%p len=%u slot=%u ssize=%u", data, data_length, slot, slot_size);
71
72 if (data_length > slot_size - 4) {
73 error("vtpm data cannot fit in data slot (%d/%d).", data_length, slot_size - 4);
74 return -1;
75 }
76
77 lenbuf = cpu_to_be32((uint32_t)data_length);
78
79 lseek(blkfront_fd, slot * slot_size, SEEK_SET);
80 if((rc = write(blkfront_fd, (uint8_t*)&lenbuf, 4)) != 4) {
81 error("write(length) failed! error was %s", strerror(errno));
82 return -1;
83 }
84 if((rc = write(blkfront_fd, data, data_length)) != data_length) {
85 error("write(data) failed! error was %s", strerror(errno));
86 return -1;
87 }
88
89 info("Wrote %u bytes to NVM persistent storage", data_length);
90
91 return 0;
92 }
93
read_vtpmblk_raw(uint8_t ** data,size_t * data_length,int slot)94 static int read_vtpmblk_raw(uint8_t **data, size_t *data_length, int slot)
95 {
96 int rc;
97 uint32_t lenbuf;
98
99 lseek(blkfront_fd, slot * slot_size, SEEK_SET);
100 if(( rc = read(blkfront_fd, (uint8_t*)&lenbuf, 4)) != 4) {
101 error("read(length) failed! error was %s", strerror(errno));
102 return -1;
103 }
104 *data_length = (size_t) cpu_to_be32(lenbuf);
105 if(*data_length == 0) {
106 error("read 0 data_length for NVM");
107 return -1;
108 }
109 if(*data_length > slot_size - 4) {
110 error("read invalid data_length for NVM");
111 return -1;
112 }
113
114 *data = tpm_malloc(*data_length);
115 if((rc = read(blkfront_fd, *data, *data_length)) != *data_length) {
116 error("read(data) failed! error was %s", strerror(errno));
117 return -1;
118 }
119
120 info("Read %u bytes from NVM persistent storage (slot %d)", *data_length, slot);
121 return 0;
122 }
123
encrypt_vtpmblk(uint8_t * clear,size_t clear_len,uint8_t ** cipher,size_t * cipher_len,uint8_t * symkey)124 int encrypt_vtpmblk(uint8_t* clear, size_t clear_len, uint8_t** cipher, size_t* cipher_len, uint8_t* symkey)
125 {
126 int rc = 0;
127 uint8_t iv[BLKSZ];
128 aes_context aes_ctx;
129 UINT32 temp;
130 int mod;
131
132 uint8_t* clbuf = NULL;
133
134 uint8_t* ivptr;
135 int ivlen;
136
137 uint8_t* cptr; //Cipher block pointer
138 int clen; //Cipher block length
139
140 /*Create a new 256 bit encryption key */
141 if(symkey == NULL) {
142 rc = -1;
143 goto abort_egress;
144 }
145 tpm_get_extern_random_bytes(symkey, NVMKEYSZ);
146
147 /*Setup initialization vector - random bits and then 4 bytes clear text size at the end*/
148 temp = sizeof(UINT32);
149 ivlen = BLKSZ - temp;
150 tpm_get_extern_random_bytes(iv, ivlen);
151 ivptr = iv + ivlen;
152 tpm_marshal_UINT32(&ivptr, &temp, (UINT32) clear_len);
153
154 /*The clear text needs to be padded out to a multiple of BLKSZ */
155 mod = clear_len % BLKSZ;
156 clen = mod ? clear_len + BLKSZ - mod : clear_len;
157 clbuf = malloc(clen);
158 if (clbuf == NULL) {
159 rc = -1;
160 goto abort_egress;
161 }
162 memcpy(clbuf, clear, clear_len);
163 /* zero out the padding bits - FIXME: better / more secure way to handle these? */
164 if(clen - clear_len) {
165 memset(clbuf + clear_len, 0, clen - clear_len);
166 }
167
168 /* Setup the ciphertext buffer */
169 *cipher_len = BLKSZ + clen; /*iv + ciphertext */
170 cptr = *cipher = malloc(*cipher_len);
171 if (*cipher == NULL) {
172 rc = -1;
173 goto abort_egress;
174 }
175
176 /* Copy the IV to cipher text blob*/
177 memcpy(cptr, iv, BLKSZ);
178 cptr += BLKSZ;
179
180 /* Setup encryption */
181 aes_setkey_enc(&aes_ctx, symkey, 256);
182
183 /* Do encryption now */
184 aes_crypt_cbc(&aes_ctx, AES_ENCRYPT, clen, iv, clbuf, cptr);
185
186 goto egress;
187 abort_egress:
188 egress:
189 free(clbuf);
190 return rc;
191 }
decrypt_vtpmblk(uint8_t * cipher,size_t cipher_len,uint8_t ** clear,size_t * clear_len,uint8_t * symkey)192 int decrypt_vtpmblk(uint8_t* cipher, size_t cipher_len, uint8_t** clear, size_t* clear_len, uint8_t* symkey)
193 {
194 int rc = 0;
195 uint8_t iv[BLKSZ];
196 uint8_t* ivptr;
197 UINT32 u32, temp;
198 aes_context aes_ctx;
199
200 uint8_t* cptr = cipher; //cipher block pointer
201 int clen = cipher_len; //cipher block length
202
203 /* Pull out the initialization vector */
204 memcpy(iv, cipher, BLKSZ);
205 cptr += BLKSZ;
206 clen -= BLKSZ;
207
208 /* Setup the clear text buffer */
209 if((*clear = malloc(clen)) == NULL) {
210 rc = -1;
211 goto abort_egress;
212 }
213
214 /* Get the length of clear text from last 4 bytes of iv */
215 temp = sizeof(UINT32);
216 ivptr = iv + BLKSZ - temp;
217 tpm_unmarshal_UINT32(&ivptr, &temp, &u32);
218 *clear_len = u32;
219
220 /* Setup decryption */
221 aes_setkey_dec(&aes_ctx, symkey, 256);
222
223 /* Do decryption now */
224 if ((clen % BLKSZ) != 0) {
225 error("Decryption Error: Cipher block size was not a multiple of %u", BLKSZ);
226 rc = -1;
227 goto abort_egress;
228 }
229 aes_crypt_cbc(&aes_ctx, AES_DECRYPT, clen, iv, cptr, *clear);
230
231 goto egress;
232 abort_egress:
233 egress:
234 return rc;
235 }
236
237 /* Current active state slot, or -1 if no valid saved state exists */
238 static int active_slot = -1;
239
write_vtpmblk(struct tpmfront_dev * tpmfront_dev,uint8_t * data,size_t data_length)240 int write_vtpmblk(struct tpmfront_dev* tpmfront_dev, uint8_t* data, size_t data_length) {
241 int rc;
242 uint8_t* cipher = NULL;
243 size_t cipher_len = 0;
244 uint8_t hashkey[HASHKEYSZ];
245 uint8_t* symkey = hashkey + HASHSZ;
246
247 /* Switch to the other slot. Note that in a new vTPM, the read will not
248 * succeed, so active_slot will be -1 and we will write to slot 0.
249 */
250 active_slot = !active_slot;
251
252 /* Encrypt the data */
253 if((rc = encrypt_vtpmblk(data, data_length, &cipher, &cipher_len, symkey))) {
254 goto abort_egress;
255 }
256 /* Write to disk */
257 if((rc = write_vtpmblk_raw(cipher, cipher_len, active_slot))) {
258 goto abort_egress;
259 }
260 /* Get sha1 hash of data */
261 sha1(cipher, cipher_len, hashkey);
262
263 /* Send hash and key to manager */
264 if((rc = VTPM_SaveHashKey(tpmfront_dev, hashkey, HASHKEYSZ)) != TPM_SUCCESS) {
265 goto abort_egress;
266 }
267 goto egress;
268 abort_egress:
269 egress:
270 free(cipher);
271 return rc;
272 }
273
read_vtpmblk(struct tpmfront_dev * tpmfront_dev,uint8_t ** data,size_t * data_length)274 int read_vtpmblk(struct tpmfront_dev* tpmfront_dev, uint8_t** data, size_t *data_length) {
275 int rc;
276 uint8_t* cipher = NULL;
277 size_t cipher_len = 0;
278 size_t keysize;
279 uint8_t* hashkey = NULL;
280 uint8_t hash0[HASHSZ];
281 uint8_t hash1[HASHSZ];
282 uint8_t* symkey;
283
284 /* Retreive the hash and the key from the manager */
285 if((rc = VTPM_LoadHashKey(tpmfront_dev, &hashkey, &keysize)) != TPM_SUCCESS) {
286 goto abort_egress;
287 }
288 if(keysize != HASHKEYSZ) {
289 error("Manager returned a hashkey of invalid size! expected %d, actual %d", NVMKEYSZ, keysize);
290 rc = -1;
291 goto abort_egress;
292 }
293 symkey = hashkey + HASHSZ;
294
295 active_slot = 0;
296 debug("Reading slot 0 from disk\n");
297 if((rc = read_vtpmblk_raw(&cipher, &cipher_len, 0))) {
298 goto abort_egress;
299 }
300
301 /* Compute the hash of the cipher text and compare */
302 sha1(cipher, cipher_len, hash0);
303 if(!memcmp(hash0, hashkey, HASHSZ))
304 goto valid;
305
306 free(cipher);
307 cipher = NULL;
308
309 active_slot = 1;
310 debug("Reading slot 1 from disk (offset=%u)\n", slot_size);
311 if((rc = read_vtpmblk_raw(&cipher, &cipher_len, 1))) {
312 goto abort_egress;
313 }
314
315 /* Compute the hash of the cipher text and compare */
316 sha1(cipher, cipher_len, hash1);
317 if(!memcmp(hash1, hashkey, HASHSZ))
318 goto valid;
319
320 {
321 int i;
322 error("NVM Storage Checksum failed!");
323 printf("Expected: ");
324 for(i = 0; i < HASHSZ; ++i) {
325 printf("%02hhX ", hashkey[i]);
326 }
327 printf("\n");
328 printf("Slot 0: ");
329 for(i = 0; i < HASHSZ; ++i) {
330 printf("%02hhX ", hash0[i]);
331 }
332 printf("\n");
333 printf("Slot 1: ");
334 for(i = 0; i < HASHSZ; ++i) {
335 printf("%02hhX ", hash1[i]);
336 }
337 printf("\n");
338 rc = -1;
339 goto abort_egress;
340 }
341 valid:
342
343 /* Decrypt the blob */
344 if((rc = decrypt_vtpmblk(cipher, cipher_len, data, data_length, symkey))) {
345 goto abort_egress;
346 }
347 goto egress;
348 abort_egress:
349 active_slot = -1;
350 egress:
351 free(cipher);
352 free(hashkey);
353 return rc;
354 }
355