1/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Host functions for keys.
6 */
7
8/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
9
10#include <openssl/pem.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15
16#include "cryptolib.h"
17#include "host_common.h"
18#include "host_key.h"
19#include "host_misc.h"
20#include "vboot_common.h"
21
22
23VbPrivateKey* PrivateKeyReadPem(const char* filename, uint64_t algorithm) {
24
25  VbPrivateKey* key;
26  RSA* rsa_key;
27  FILE* f;
28
29  if (algorithm >= kNumAlgorithms) {
30    VBDEBUG(("%s() called with invalid algorithm!\n", __FUNCTION__));
31    return NULL;
32  }
33
34  /* Read private key */
35  f = fopen(filename, "r");
36  if (!f) {
37    VBDEBUG(("%s(): Couldn't open key file: %s\n", __FUNCTION__, filename));
38    return NULL;
39  }
40  rsa_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
41  fclose(f);
42  if (!rsa_key) {
43    VBDEBUG(("%s(): Couldn't read private key from file: %s\n", __FUNCTION__,
44             filename));
45    return NULL;
46  }
47
48  /* Store key and algorithm in our struct */
49  key = (VbPrivateKey*)malloc(sizeof(VbPrivateKey));
50  if (!key) {
51    RSA_free(rsa_key);
52    return NULL;
53  }
54  key->rsa_private_key = rsa_key;
55  key->algorithm = algorithm;
56
57  /* Return the key */
58  return key;
59}
60
61
62void PrivateKeyFree(VbPrivateKey* key) {
63  if (!key)
64    return;
65  if (key->rsa_private_key)
66    RSA_free(key->rsa_private_key);
67  free(key);
68}
69
70
71/* Write a private key to a file in .vbprivk format. */
72int PrivateKeyWrite(const char* filename, const VbPrivateKey* key) {
73  uint8_t *outbuf = 0;
74  int buflen;
75  FILE *f;
76
77  buflen = i2d_RSAPrivateKey(key->rsa_private_key, &outbuf);
78  if (buflen <= 0) {
79    VbExError("Unable to write private key buffer\n");
80    return 1;
81  }
82
83  f = fopen(filename, "wb");
84  if (!f) {
85    VbExError("Unable to open file %s\n", filename);
86    free(outbuf);
87    return 1;
88  }
89
90  if (1 != fwrite(&key->algorithm, sizeof(key->algorithm), 1, f)) {
91    VbExError("Unable to write to file %s\n", filename);
92    fclose(f);
93    free(outbuf);
94    unlink(filename);  /* Delete any partial file */
95  }
96
97  if (1 != fwrite(outbuf, buflen, 1, f)) {
98    VbExError("Unable to write to file %s\n", filename);
99    fclose(f);
100    unlink(filename);  /* Delete any partial file */
101    free(outbuf);
102  }
103
104  fclose(f);
105  free(outbuf);
106  return 0;
107}
108
109VbPrivateKey* PrivateKeyRead(const char* filename) {
110  VbPrivateKey *key;
111  uint64_t filelen = 0;
112  uint8_t *buffer;
113  const unsigned char *start;
114
115  buffer = ReadFile(filename, &filelen);
116  if (!buffer) {
117    VbExError("unable to read from file %s\n", filename);
118    return 0;
119  }
120
121  key = (VbPrivateKey*)malloc(sizeof(VbPrivateKey));
122  if (!key) {
123    VbExError("Unable to allocate VbPrivateKey\n");
124    free(buffer);
125    return 0;
126  }
127
128  key->algorithm = *(typeof(key->algorithm) *)buffer;
129  start = buffer + sizeof(key->algorithm);
130
131  key->rsa_private_key = d2i_RSAPrivateKey(0, &start,
132                                           filelen - sizeof(key->algorithm));
133
134  if (!key->rsa_private_key) {
135    VbExError("Unable to parse RSA private key\n");
136    free(buffer);
137    free(key);
138    return 0;
139  }
140
141  free(buffer);
142  return key;
143}
144
145
146/* Allocate a new public key with space for a [key_size] byte key. */
147VbPublicKey* PublicKeyAlloc(uint64_t key_size, uint64_t algorithm,
148                            uint64_t version) {
149  VbPublicKey* key = (VbPublicKey*)malloc(sizeof(VbPublicKey) + key_size);
150  if (!key)
151    return NULL;
152
153  key->algorithm = algorithm;
154  key->key_version = version;
155  key->key_size = key_size;
156  key->key_offset = sizeof(VbPublicKey);
157  return key;
158}
159
160VbPublicKey* PublicKeyReadKeyb(const char* filename, uint64_t algorithm,
161                               uint64_t version) {
162  VbPublicKey* key;
163  uint8_t* key_data;
164  uint64_t key_size;
165  uint64_t expected_key_size;
166
167  if (algorithm >= kNumAlgorithms) {
168    VBDEBUG(("PublicKeyReadKeyb() called with invalid algorithm!\n"));
169    return NULL;
170  }
171  if (version > 0xFFFF) {
172    /* Currently, TPM only supports 16-bit version */
173    VBDEBUG(("PublicKeyReadKeyb() called with invalid version!\n"));
174    return NULL;
175  }
176
177  key_data = ReadFile(filename, &key_size);
178  if (!key_data)
179    return NULL;
180
181  if (!RSAProcessedKeySize(algorithm, &expected_key_size) ||
182      expected_key_size != key_size) {
183    VBDEBUG(("PublicKeyReadKeyb() wrong key size for algorithm\n"));
184    free(key_data);
185    return NULL;
186  }
187
188  key = PublicKeyAlloc(key_size, algorithm, version);
189  if (!key) {
190    free(key_data);
191    return NULL;
192  }
193  Memcpy(GetPublicKeyData(key), key_data, key_size);
194
195  free(key_data);
196  return key;
197}
198
199
200int PublicKeyLooksOkay(VbPublicKey *key, uint64_t file_size)
201{
202  uint64_t key_size;
203
204  /* Sanity-check key data */
205  if (0 != VerifyPublicKeyInside(key, file_size, key)) {
206    VBDEBUG(("PublicKeyRead() not a VbPublicKey\n"));
207    return 0;
208  }
209  if (key->algorithm >= kNumAlgorithms) {
210    VBDEBUG(("PublicKeyRead() invalid algorithm\n"));
211    return 0;
212  }
213  if (key->key_version > 0xFFFF) {
214    VBDEBUG(("PublicKeyRead() invalid version\n"));
215    return 0;  /* Currently, TPM only supports 16-bit version */
216  }
217  if (!RSAProcessedKeySize(key->algorithm, &key_size) ||
218      key_size != key->key_size) {
219    VBDEBUG(("PublicKeyRead() wrong key size for algorithm\n"));
220    return 0;
221  }
222
223  /* Success */
224  return 1;
225}
226
227
228
229VbPublicKey* PublicKeyRead(const char* filename) {
230  VbPublicKey* key;
231  uint64_t file_size;
232
233  key = (VbPublicKey*)ReadFile(filename, &file_size);
234  if (!key)
235    return NULL;
236
237  if (PublicKeyLooksOkay(key, file_size))
238      return key;
239
240  /* Error */
241  free(key);
242  return NULL;
243}
244
245int PublicKeyWrite(const char* filename, const VbPublicKey* key) {
246  VbPublicKey* kcopy;
247  int rv;
248
249  /* Copy the key, so its data is contiguous with the header */
250  kcopy = PublicKeyAlloc(key->key_size, 0, 0);
251  if (!kcopy)
252    return 1;
253  if (0 != PublicKeyCopy(kcopy, key)) {
254    free(kcopy);
255    return 1;
256  }
257
258  /* Write the copy, then free it */
259  rv = WriteFile(filename, kcopy, kcopy->key_offset + kcopy->key_size);
260  free(kcopy);
261  return rv;
262}
263