Rough and ready port of axTLS
ssl/p12.c@0:5a29fd060ac8, 2013-05-13 (annotated)
- Committer:
- ashleymills
- Date:
- Mon May 13 18:15:18 2013 +0000
- Revision:
- 0:5a29fd060ac8
initial commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
ashleymills | 0:5a29fd060ac8 | 1 | /* |
ashleymills | 0:5a29fd060ac8 | 2 | * Copyright (c) 2007, Cameron Rich |
ashleymills | 0:5a29fd060ac8 | 3 | * |
ashleymills | 0:5a29fd060ac8 | 4 | * All rights reserved. |
ashleymills | 0:5a29fd060ac8 | 5 | * |
ashleymills | 0:5a29fd060ac8 | 6 | * Redistribution and use in source and binary forms, with or without |
ashleymills | 0:5a29fd060ac8 | 7 | * modification, are permitted provided that the following conditions are met: |
ashleymills | 0:5a29fd060ac8 | 8 | * |
ashleymills | 0:5a29fd060ac8 | 9 | * * Redistributions of source code must retain the above copyright notice, |
ashleymills | 0:5a29fd060ac8 | 10 | * this list of conditions and the following disclaimer. |
ashleymills | 0:5a29fd060ac8 | 11 | * * Redistributions in binary form must reproduce the above copyright notice, |
ashleymills | 0:5a29fd060ac8 | 12 | * this list of conditions and the following disclaimer in the documentation |
ashleymills | 0:5a29fd060ac8 | 13 | * and/or other materials provided with the distribution. |
ashleymills | 0:5a29fd060ac8 | 14 | * * Neither the name of the axTLS project nor the names of its contributors |
ashleymills | 0:5a29fd060ac8 | 15 | * may be used to endorse or promote products derived from this software |
ashleymills | 0:5a29fd060ac8 | 16 | * without specific prior written permission. |
ashleymills | 0:5a29fd060ac8 | 17 | * |
ashleymills | 0:5a29fd060ac8 | 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
ashleymills | 0:5a29fd060ac8 | 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
ashleymills | 0:5a29fd060ac8 | 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
ashleymills | 0:5a29fd060ac8 | 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
ashleymills | 0:5a29fd060ac8 | 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
ashleymills | 0:5a29fd060ac8 | 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
ashleymills | 0:5a29fd060ac8 | 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
ashleymills | 0:5a29fd060ac8 | 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
ashleymills | 0:5a29fd060ac8 | 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
ashleymills | 0:5a29fd060ac8 | 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
ashleymills | 0:5a29fd060ac8 | 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
ashleymills | 0:5a29fd060ac8 | 29 | */ |
ashleymills | 0:5a29fd060ac8 | 30 | |
ashleymills | 0:5a29fd060ac8 | 31 | /** |
ashleymills | 0:5a29fd060ac8 | 32 | * Process PKCS#8/PKCS#12 keys. |
ashleymills | 0:5a29fd060ac8 | 33 | * |
ashleymills | 0:5a29fd060ac8 | 34 | * The decoding of a PKCS#12 key is fairly specific - this code was tested on a |
ashleymills | 0:5a29fd060ac8 | 35 | * key generated with: |
ashleymills | 0:5a29fd060ac8 | 36 | * |
ashleymills | 0:5a29fd060ac8 | 37 | * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem |
ashleymills | 0:5a29fd060ac8 | 38 | * -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 |
ashleymills | 0:5a29fd060ac8 | 39 | * -name "p12_withoutCA" -out axTLS.withoutCA.p12 -password pass:abcd |
ashleymills | 0:5a29fd060ac8 | 40 | * |
ashleymills | 0:5a29fd060ac8 | 41 | * or with a certificate chain: |
ashleymills | 0:5a29fd060ac8 | 42 | * |
ashleymills | 0:5a29fd060ac8 | 43 | * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem |
ashleymills | 0:5a29fd060ac8 | 44 | * -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe |
ashleymills | 0:5a29fd060ac8 | 45 | * PBE-SHA1-RC4-128 -name "p12_withCA" -out axTLS.withCA.p12 -password pass:abcd |
ashleymills | 0:5a29fd060ac8 | 46 | * |
ashleymills | 0:5a29fd060ac8 | 47 | * Note that the PBE has to be specified with PBE-SHA1-RC4-128. The |
ashleymills | 0:5a29fd060ac8 | 48 | * private/public keys/certs have to use RSA encryption. Both the integrity |
ashleymills | 0:5a29fd060ac8 | 49 | * and privacy passwords are the same. |
ashleymills | 0:5a29fd060ac8 | 50 | * |
ashleymills | 0:5a29fd060ac8 | 51 | * The PKCS#8 files were generated with something like: |
ashleymills | 0:5a29fd060ac8 | 52 | * |
ashleymills | 0:5a29fd060ac8 | 53 | * PEM format: |
ashleymills | 0:5a29fd060ac8 | 54 | * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1 |
ashleymills | 0:5a29fd060ac8 | 55 | * PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8 |
ashleymills | 0:5a29fd060ac8 | 56 | * |
ashleymills | 0:5a29fd060ac8 | 57 | * DER format: |
ashleymills | 0:5a29fd060ac8 | 58 | * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER |
ashleymills | 0:5a29fd060ac8 | 59 | * -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8 |
ashleymills | 0:5a29fd060ac8 | 60 | */ |
ashleymills | 0:5a29fd060ac8 | 61 | |
ashleymills | 0:5a29fd060ac8 | 62 | #include <stdlib.h> |
ashleymills | 0:5a29fd060ac8 | 63 | #include <string.h> |
ashleymills | 0:5a29fd060ac8 | 64 | #include <stdio.h> |
ashleymills | 0:5a29fd060ac8 | 65 | #include "os_port.h" |
ashleymills | 0:5a29fd060ac8 | 66 | #include "ssl.h" |
ashleymills | 0:5a29fd060ac8 | 67 | |
ashleymills | 0:5a29fd060ac8 | 68 | /* all commented out if not used */ |
ashleymills | 0:5a29fd060ac8 | 69 | #ifdef CONFIG_SSL_USE_PKCS12 |
ashleymills | 0:5a29fd060ac8 | 70 | |
ashleymills | 0:5a29fd060ac8 | 71 | #define BLOCK_SIZE 64 |
ashleymills | 0:5a29fd060ac8 | 72 | #define PKCS12_KEY_ID 1 |
ashleymills | 0:5a29fd060ac8 | 73 | #define PKCS12_IV_ID 2 |
ashleymills | 0:5a29fd060ac8 | 74 | #define PKCS12_MAC_ID 3 |
ashleymills | 0:5a29fd060ac8 | 75 | |
ashleymills | 0:5a29fd060ac8 | 76 | static char *make_uni_pass(const char *password, int *uni_pass_len); |
ashleymills | 0:5a29fd060ac8 | 77 | static int p8_decrypt(const char *uni_pass, int uni_pass_len, |
ashleymills | 0:5a29fd060ac8 | 78 | const uint8_t *salt, int iter, |
ashleymills | 0:5a29fd060ac8 | 79 | uint8_t *priv_key, int priv_key_len, int id); |
ashleymills | 0:5a29fd060ac8 | 80 | static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key); |
ashleymills | 0:5a29fd060ac8 | 81 | static int get_pbe_params(uint8_t *buf, int *offset, |
ashleymills | 0:5a29fd060ac8 | 82 | const uint8_t **salt, int *iterations); |
ashleymills | 0:5a29fd060ac8 | 83 | |
ashleymills | 0:5a29fd060ac8 | 84 | /* |
ashleymills | 0:5a29fd060ac8 | 85 | * Take a raw pkcs8 block and then decrypt it and turn it into a normal key. |
ashleymills | 0:5a29fd060ac8 | 86 | */ |
ashleymills | 0:5a29fd060ac8 | 87 | int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) |
ashleymills | 0:5a29fd060ac8 | 88 | { |
ashleymills | 0:5a29fd060ac8 | 89 | uint8_t *buf = ssl_obj->buf; |
ashleymills | 0:5a29fd060ac8 | 90 | int len, offset = 0; |
ashleymills | 0:5a29fd060ac8 | 91 | int iterations; |
ashleymills | 0:5a29fd060ac8 | 92 | int ret = SSL_NOT_OK; |
ashleymills | 0:5a29fd060ac8 | 93 | uint8_t *version = NULL; |
ashleymills | 0:5a29fd060ac8 | 94 | const uint8_t *salt; |
ashleymills | 0:5a29fd060ac8 | 95 | uint8_t *priv_key; |
ashleymills | 0:5a29fd060ac8 | 96 | int uni_pass_len; |
ashleymills | 0:5a29fd060ac8 | 97 | char *uni_pass = make_uni_pass(password, &uni_pass_len); |
ashleymills | 0:5a29fd060ac8 | 98 | |
ashleymills | 0:5a29fd060ac8 | 99 | if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) |
ashleymills | 0:5a29fd060ac8 | 100 | { |
ashleymills | 0:5a29fd060ac8 | 101 | #ifdef CONFIG_SSL_FULL_MODE |
ashleymills | 0:5a29fd060ac8 | 102 | printf("Error: Invalid p8 ASN.1 file\n"); |
ashleymills | 0:5a29fd060ac8 | 103 | #endif |
ashleymills | 0:5a29fd060ac8 | 104 | goto error; |
ashleymills | 0:5a29fd060ac8 | 105 | } |
ashleymills | 0:5a29fd060ac8 | 106 | |
ashleymills | 0:5a29fd060ac8 | 107 | /* unencrypted key? */ |
ashleymills | 0:5a29fd060ac8 | 108 | if (asn1_get_int(buf, &offset, &version) > 0 && *version == 0) |
ashleymills | 0:5a29fd060ac8 | 109 | { |
ashleymills | 0:5a29fd060ac8 | 110 | ret = p8_add_key(ssl_ctx, buf); |
ashleymills | 0:5a29fd060ac8 | 111 | goto error; |
ashleymills | 0:5a29fd060ac8 | 112 | } |
ashleymills | 0:5a29fd060ac8 | 113 | |
ashleymills | 0:5a29fd060ac8 | 114 | if (get_pbe_params(buf, &offset, &salt, &iterations) < 0) |
ashleymills | 0:5a29fd060ac8 | 115 | goto error; |
ashleymills | 0:5a29fd060ac8 | 116 | |
ashleymills | 0:5a29fd060ac8 | 117 | if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) |
ashleymills | 0:5a29fd060ac8 | 118 | goto error; |
ashleymills | 0:5a29fd060ac8 | 119 | |
ashleymills | 0:5a29fd060ac8 | 120 | priv_key = &buf[offset]; |
ashleymills | 0:5a29fd060ac8 | 121 | |
ashleymills | 0:5a29fd060ac8 | 122 | p8_decrypt(uni_pass, uni_pass_len, salt, |
ashleymills | 0:5a29fd060ac8 | 123 | iterations, priv_key, len, PKCS12_KEY_ID); |
ashleymills | 0:5a29fd060ac8 | 124 | ret = p8_add_key(ssl_ctx, priv_key); |
ashleymills | 0:5a29fd060ac8 | 125 | |
ashleymills | 0:5a29fd060ac8 | 126 | error: |
ashleymills | 0:5a29fd060ac8 | 127 | free(version); |
ashleymills | 0:5a29fd060ac8 | 128 | free(uni_pass); |
ashleymills | 0:5a29fd060ac8 | 129 | return ret; |
ashleymills | 0:5a29fd060ac8 | 130 | } |
ashleymills | 0:5a29fd060ac8 | 131 | |
ashleymills | 0:5a29fd060ac8 | 132 | /* |
ashleymills | 0:5a29fd060ac8 | 133 | * Take the unencrypted pkcs8 and turn it into a private key |
ashleymills | 0:5a29fd060ac8 | 134 | */ |
ashleymills | 0:5a29fd060ac8 | 135 | static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key) |
ashleymills | 0:5a29fd060ac8 | 136 | { |
ashleymills | 0:5a29fd060ac8 | 137 | uint8_t *buf = priv_key; |
ashleymills | 0:5a29fd060ac8 | 138 | int len, offset = 0; |
ashleymills | 0:5a29fd060ac8 | 139 | int ret = SSL_NOT_OK; |
ashleymills | 0:5a29fd060ac8 | 140 | |
ashleymills | 0:5a29fd060ac8 | 141 | /* Skip the preamble and go straight to the private key. |
ashleymills | 0:5a29fd060ac8 | 142 | We only support rsaEncryption (1.2.840.113549.1.1.1) */ |
ashleymills | 0:5a29fd060ac8 | 143 | if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 144 | asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || |
ashleymills | 0:5a29fd060ac8 | 145 | asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 146 | (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) |
ashleymills | 0:5a29fd060ac8 | 147 | goto error; |
ashleymills | 0:5a29fd060ac8 | 148 | |
ashleymills | 0:5a29fd060ac8 | 149 | ret = asn1_get_private_key(&buf[offset], len, &ssl_ctx->rsa_ctx); |
ashleymills | 0:5a29fd060ac8 | 150 | |
ashleymills | 0:5a29fd060ac8 | 151 | error: |
ashleymills | 0:5a29fd060ac8 | 152 | return ret; |
ashleymills | 0:5a29fd060ac8 | 153 | } |
ashleymills | 0:5a29fd060ac8 | 154 | |
ashleymills | 0:5a29fd060ac8 | 155 | /* |
ashleymills | 0:5a29fd060ac8 | 156 | * Create the unicode password |
ashleymills | 0:5a29fd060ac8 | 157 | */ |
ashleymills | 0:5a29fd060ac8 | 158 | static char *make_uni_pass(const char *password, int *uni_pass_len) |
ashleymills | 0:5a29fd060ac8 | 159 | { |
ashleymills | 0:5a29fd060ac8 | 160 | int pass_len = 0, i; |
ashleymills | 0:5a29fd060ac8 | 161 | char *uni_pass; |
ashleymills | 0:5a29fd060ac8 | 162 | |
ashleymills | 0:5a29fd060ac8 | 163 | if (password == NULL) |
ashleymills | 0:5a29fd060ac8 | 164 | { |
ashleymills | 0:5a29fd060ac8 | 165 | password = ""; |
ashleymills | 0:5a29fd060ac8 | 166 | } |
ashleymills | 0:5a29fd060ac8 | 167 | |
ashleymills | 0:5a29fd060ac8 | 168 | uni_pass = (char *)malloc((strlen(password)+1)*2); |
ashleymills | 0:5a29fd060ac8 | 169 | |
ashleymills | 0:5a29fd060ac8 | 170 | /* modify the password into a unicode version */ |
ashleymills | 0:5a29fd060ac8 | 171 | for (i = 0; i < (int)strlen(password); i++) |
ashleymills | 0:5a29fd060ac8 | 172 | { |
ashleymills | 0:5a29fd060ac8 | 173 | uni_pass[pass_len++] = 0; |
ashleymills | 0:5a29fd060ac8 | 174 | uni_pass[pass_len++] = password[i]; |
ashleymills | 0:5a29fd060ac8 | 175 | } |
ashleymills | 0:5a29fd060ac8 | 176 | |
ashleymills | 0:5a29fd060ac8 | 177 | uni_pass[pass_len++] = 0; /* null terminate */ |
ashleymills | 0:5a29fd060ac8 | 178 | uni_pass[pass_len++] = 0; |
ashleymills | 0:5a29fd060ac8 | 179 | *uni_pass_len = pass_len; |
ashleymills | 0:5a29fd060ac8 | 180 | return uni_pass; |
ashleymills | 0:5a29fd060ac8 | 181 | } |
ashleymills | 0:5a29fd060ac8 | 182 | |
ashleymills | 0:5a29fd060ac8 | 183 | /* |
ashleymills | 0:5a29fd060ac8 | 184 | * Decrypt a pkcs8 block. |
ashleymills | 0:5a29fd060ac8 | 185 | */ |
ashleymills | 0:5a29fd060ac8 | 186 | static int p8_decrypt(const char *uni_pass, int uni_pass_len, |
ashleymills | 0:5a29fd060ac8 | 187 | const uint8_t *salt, int iter, |
ashleymills | 0:5a29fd060ac8 | 188 | uint8_t *priv_key, int priv_key_len, int id) |
ashleymills | 0:5a29fd060ac8 | 189 | { |
ashleymills | 0:5a29fd060ac8 | 190 | uint8_t p[BLOCK_SIZE*2]; |
ashleymills | 0:5a29fd060ac8 | 191 | uint8_t d[BLOCK_SIZE]; |
ashleymills | 0:5a29fd060ac8 | 192 | uint8_t Ai[SHA1_SIZE]; |
ashleymills | 0:5a29fd060ac8 | 193 | SHA1_CTX sha_ctx; |
ashleymills | 0:5a29fd060ac8 | 194 | RC4_CTX rc4_ctx; |
ashleymills | 0:5a29fd060ac8 | 195 | int i; |
ashleymills | 0:5a29fd060ac8 | 196 | |
ashleymills | 0:5a29fd060ac8 | 197 | for (i = 0; i < BLOCK_SIZE; i++) |
ashleymills | 0:5a29fd060ac8 | 198 | { |
ashleymills | 0:5a29fd060ac8 | 199 | p[i] = salt[i % SALT_SIZE]; |
ashleymills | 0:5a29fd060ac8 | 200 | p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len]; |
ashleymills | 0:5a29fd060ac8 | 201 | d[i] = id; |
ashleymills | 0:5a29fd060ac8 | 202 | } |
ashleymills | 0:5a29fd060ac8 | 203 | |
ashleymills | 0:5a29fd060ac8 | 204 | /* get the key - no IV since we are using RC4 */ |
ashleymills | 0:5a29fd060ac8 | 205 | SHA1_Init(&sha_ctx); |
ashleymills | 0:5a29fd060ac8 | 206 | SHA1_Update(&sha_ctx, d, sizeof(d)); |
ashleymills | 0:5a29fd060ac8 | 207 | SHA1_Update(&sha_ctx, p, sizeof(p)); |
ashleymills | 0:5a29fd060ac8 | 208 | SHA1_Final(Ai, &sha_ctx); |
ashleymills | 0:5a29fd060ac8 | 209 | |
ashleymills | 0:5a29fd060ac8 | 210 | for (i = 1; i < iter; i++) |
ashleymills | 0:5a29fd060ac8 | 211 | { |
ashleymills | 0:5a29fd060ac8 | 212 | SHA1_Init(&sha_ctx); |
ashleymills | 0:5a29fd060ac8 | 213 | SHA1_Update(&sha_ctx, Ai, SHA1_SIZE); |
ashleymills | 0:5a29fd060ac8 | 214 | SHA1_Final(Ai, &sha_ctx); |
ashleymills | 0:5a29fd060ac8 | 215 | } |
ashleymills | 0:5a29fd060ac8 | 216 | |
ashleymills | 0:5a29fd060ac8 | 217 | /* do the decryption */ |
ashleymills | 0:5a29fd060ac8 | 218 | if (id == PKCS12_KEY_ID) |
ashleymills | 0:5a29fd060ac8 | 219 | { |
ashleymills | 0:5a29fd060ac8 | 220 | RC4_setup(&rc4_ctx, Ai, 16); |
ashleymills | 0:5a29fd060ac8 | 221 | RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len); |
ashleymills | 0:5a29fd060ac8 | 222 | } |
ashleymills | 0:5a29fd060ac8 | 223 | else /* MAC */ |
ashleymills | 0:5a29fd060ac8 | 224 | memcpy(priv_key, Ai, SHA1_SIZE); |
ashleymills | 0:5a29fd060ac8 | 225 | |
ashleymills | 0:5a29fd060ac8 | 226 | return 0; |
ashleymills | 0:5a29fd060ac8 | 227 | } |
ashleymills | 0:5a29fd060ac8 | 228 | |
ashleymills | 0:5a29fd060ac8 | 229 | /* |
ashleymills | 0:5a29fd060ac8 | 230 | * Take a raw pkcs12 block and the decrypt it and turn it into a certificate(s) |
ashleymills | 0:5a29fd060ac8 | 231 | * and keys. |
ashleymills | 0:5a29fd060ac8 | 232 | */ |
ashleymills | 0:5a29fd060ac8 | 233 | int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) |
ashleymills | 0:5a29fd060ac8 | 234 | { |
ashleymills | 0:5a29fd060ac8 | 235 | uint8_t *buf = ssl_obj->buf; |
ashleymills | 0:5a29fd060ac8 | 236 | int len, iterations, auth_safes_start, |
ashleymills | 0:5a29fd060ac8 | 237 | auth_safes_end, auth_safes_len, key_offset, offset = 0; |
ashleymills | 0:5a29fd060ac8 | 238 | int all_certs = 0; |
ashleymills | 0:5a29fd060ac8 | 239 | uint8_t *version = NULL, *auth_safes = NULL, *cert, *orig_mac; |
ashleymills | 0:5a29fd060ac8 | 240 | uint8_t key[SHA1_SIZE]; |
ashleymills | 0:5a29fd060ac8 | 241 | uint8_t mac[SHA1_SIZE]; |
ashleymills | 0:5a29fd060ac8 | 242 | const uint8_t *salt; |
ashleymills | 0:5a29fd060ac8 | 243 | int uni_pass_len, ret = SSL_OK; |
ashleymills | 0:5a29fd060ac8 | 244 | char *uni_pass = make_uni_pass(password, &uni_pass_len); |
ashleymills | 0:5a29fd060ac8 | 245 | static const uint8_t pkcs_data[] = /* pkc7 data */ |
ashleymills | 0:5a29fd060ac8 | 246 | { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 }; |
ashleymills | 0:5a29fd060ac8 | 247 | static const uint8_t pkcs_encrypted[] = /* pkc7 encrypted */ |
ashleymills | 0:5a29fd060ac8 | 248 | { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 }; |
ashleymills | 0:5a29fd060ac8 | 249 | static const uint8_t pkcs8_key_bag[] = /* 1.2.840.113549.1.12.10.1.2 */ |
ashleymills | 0:5a29fd060ac8 | 250 | { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 }; |
ashleymills | 0:5a29fd060ac8 | 251 | |
ashleymills | 0:5a29fd060ac8 | 252 | if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) |
ashleymills | 0:5a29fd060ac8 | 253 | { |
ashleymills | 0:5a29fd060ac8 | 254 | #ifdef CONFIG_SSL_FULL_MODE |
ashleymills | 0:5a29fd060ac8 | 255 | printf("Error: Invalid p12 ASN.1 file\n"); |
ashleymills | 0:5a29fd060ac8 | 256 | #endif |
ashleymills | 0:5a29fd060ac8 | 257 | goto error; |
ashleymills | 0:5a29fd060ac8 | 258 | } |
ashleymills | 0:5a29fd060ac8 | 259 | |
ashleymills | 0:5a29fd060ac8 | 260 | if (asn1_get_int(buf, &offset, &version) < 0 || *version != 3) |
ashleymills | 0:5a29fd060ac8 | 261 | { |
ashleymills | 0:5a29fd060ac8 | 262 | ret = SSL_ERROR_INVALID_VERSION; |
ashleymills | 0:5a29fd060ac8 | 263 | goto error; |
ashleymills | 0:5a29fd060ac8 | 264 | } |
ashleymills | 0:5a29fd060ac8 | 265 | |
ashleymills | 0:5a29fd060ac8 | 266 | /* remove all the boring pcks7 bits */ |
ashleymills | 0:5a29fd060ac8 | 267 | if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 268 | (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || |
ashleymills | 0:5a29fd060ac8 | 269 | len != sizeof(pkcs_data) || |
ashleymills | 0:5a29fd060ac8 | 270 | memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) |
ashleymills | 0:5a29fd060ac8 | 271 | goto error; |
ashleymills | 0:5a29fd060ac8 | 272 | |
ashleymills | 0:5a29fd060ac8 | 273 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 274 | |
ashleymills | 0:5a29fd060ac8 | 275 | if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || |
ashleymills | 0:5a29fd060ac8 | 276 | asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0) |
ashleymills | 0:5a29fd060ac8 | 277 | goto error; |
ashleymills | 0:5a29fd060ac8 | 278 | |
ashleymills | 0:5a29fd060ac8 | 279 | /* work out the MAC start/end points (done on AuthSafes) */ |
ashleymills | 0:5a29fd060ac8 | 280 | auth_safes_start = offset; |
ashleymills | 0:5a29fd060ac8 | 281 | auth_safes_end = offset; |
ashleymills | 0:5a29fd060ac8 | 282 | if (asn1_skip_obj(buf, &auth_safes_end, ASN1_SEQUENCE) < 0) |
ashleymills | 0:5a29fd060ac8 | 283 | goto error; |
ashleymills | 0:5a29fd060ac8 | 284 | |
ashleymills | 0:5a29fd060ac8 | 285 | auth_safes_len = auth_safes_end - auth_safes_start; |
ashleymills | 0:5a29fd060ac8 | 286 | auth_safes = malloc(auth_safes_len); |
ashleymills | 0:5a29fd060ac8 | 287 | |
ashleymills | 0:5a29fd060ac8 | 288 | memcpy(auth_safes, &buf[auth_safes_start], auth_safes_len); |
ashleymills | 0:5a29fd060ac8 | 289 | |
ashleymills | 0:5a29fd060ac8 | 290 | if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 291 | asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 292 | (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || |
ashleymills | 0:5a29fd060ac8 | 293 | (len != sizeof(pkcs_encrypted) || |
ashleymills | 0:5a29fd060ac8 | 294 | memcmp(&buf[offset], pkcs_encrypted, sizeof(pkcs_encrypted)))) |
ashleymills | 0:5a29fd060ac8 | 295 | goto error; |
ashleymills | 0:5a29fd060ac8 | 296 | |
ashleymills | 0:5a29fd060ac8 | 297 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 298 | |
ashleymills | 0:5a29fd060ac8 | 299 | if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || |
ashleymills | 0:5a29fd060ac8 | 300 | asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 301 | asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || |
ashleymills | 0:5a29fd060ac8 | 302 | asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 303 | (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || |
ashleymills | 0:5a29fd060ac8 | 304 | len != sizeof(pkcs_data) || |
ashleymills | 0:5a29fd060ac8 | 305 | memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) |
ashleymills | 0:5a29fd060ac8 | 306 | goto error; |
ashleymills | 0:5a29fd060ac8 | 307 | |
ashleymills | 0:5a29fd060ac8 | 308 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 309 | |
ashleymills | 0:5a29fd060ac8 | 310 | /* work out the salt for the certificate */ |
ashleymills | 0:5a29fd060ac8 | 311 | if (get_pbe_params(buf, &offset, &salt, &iterations) < 0 || |
ashleymills | 0:5a29fd060ac8 | 312 | (len = asn1_next_obj(buf, &offset, ASN1_IMPLICIT_TAG)) < 0) |
ashleymills | 0:5a29fd060ac8 | 313 | goto error; |
ashleymills | 0:5a29fd060ac8 | 314 | |
ashleymills | 0:5a29fd060ac8 | 315 | /* decrypt the certificate */ |
ashleymills | 0:5a29fd060ac8 | 316 | cert = &buf[offset]; |
ashleymills | 0:5a29fd060ac8 | 317 | if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, |
ashleymills | 0:5a29fd060ac8 | 318 | len, PKCS12_KEY_ID)) < 0) |
ashleymills | 0:5a29fd060ac8 | 319 | goto error; |
ashleymills | 0:5a29fd060ac8 | 320 | |
ashleymills | 0:5a29fd060ac8 | 321 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 322 | |
ashleymills | 0:5a29fd060ac8 | 323 | /* load the certificate */ |
ashleymills | 0:5a29fd060ac8 | 324 | key_offset = 0; |
ashleymills | 0:5a29fd060ac8 | 325 | all_certs = asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE); |
ashleymills | 0:5a29fd060ac8 | 326 | |
ashleymills | 0:5a29fd060ac8 | 327 | /* keep going until all certs are loaded */ |
ashleymills | 0:5a29fd060ac8 | 328 | while (key_offset < all_certs) |
ashleymills | 0:5a29fd060ac8 | 329 | { |
ashleymills | 0:5a29fd060ac8 | 330 | int cert_offset = key_offset; |
ashleymills | 0:5a29fd060ac8 | 331 | |
ashleymills | 0:5a29fd060ac8 | 332 | if (asn1_skip_obj(cert, &cert_offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 333 | asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 334 | asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || |
ashleymills | 0:5a29fd060ac8 | 335 | asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || |
ashleymills | 0:5a29fd060ac8 | 336 | asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 337 | asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || |
ashleymills | 0:5a29fd060ac8 | 338 | asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || |
ashleymills | 0:5a29fd060ac8 | 339 | (len = asn1_next_obj(cert, &key_offset, ASN1_OCTET_STRING)) < 0) |
ashleymills | 0:5a29fd060ac8 | 340 | goto error; |
ashleymills | 0:5a29fd060ac8 | 341 | |
ashleymills | 0:5a29fd060ac8 | 342 | if ((ret = add_cert(ssl_ctx, &cert[key_offset], len)) < 0) |
ashleymills | 0:5a29fd060ac8 | 343 | goto error; |
ashleymills | 0:5a29fd060ac8 | 344 | |
ashleymills | 0:5a29fd060ac8 | 345 | key_offset = cert_offset; |
ashleymills | 0:5a29fd060ac8 | 346 | } |
ashleymills | 0:5a29fd060ac8 | 347 | |
ashleymills | 0:5a29fd060ac8 | 348 | if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 349 | (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || |
ashleymills | 0:5a29fd060ac8 | 350 | len != sizeof(pkcs_data) || |
ashleymills | 0:5a29fd060ac8 | 351 | memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) |
ashleymills | 0:5a29fd060ac8 | 352 | goto error; |
ashleymills | 0:5a29fd060ac8 | 353 | |
ashleymills | 0:5a29fd060ac8 | 354 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 355 | |
ashleymills | 0:5a29fd060ac8 | 356 | if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || |
ashleymills | 0:5a29fd060ac8 | 357 | asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0 || |
ashleymills | 0:5a29fd060ac8 | 358 | asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 359 | asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 360 | (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || |
ashleymills | 0:5a29fd060ac8 | 361 | (len != sizeof(pkcs8_key_bag)) || |
ashleymills | 0:5a29fd060ac8 | 362 | memcmp(&buf[offset], pkcs8_key_bag, sizeof(pkcs8_key_bag))) |
ashleymills | 0:5a29fd060ac8 | 363 | goto error; |
ashleymills | 0:5a29fd060ac8 | 364 | |
ashleymills | 0:5a29fd060ac8 | 365 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 366 | |
ashleymills | 0:5a29fd060ac8 | 367 | /* work out the salt for the private key */ |
ashleymills | 0:5a29fd060ac8 | 368 | if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || |
ashleymills | 0:5a29fd060ac8 | 369 | asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 370 | get_pbe_params(buf, &offset, &salt, &iterations) < 0 || |
ashleymills | 0:5a29fd060ac8 | 371 | (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) |
ashleymills | 0:5a29fd060ac8 | 372 | goto error; |
ashleymills | 0:5a29fd060ac8 | 373 | |
ashleymills | 0:5a29fd060ac8 | 374 | /* decrypt the private key */ |
ashleymills | 0:5a29fd060ac8 | 375 | cert = &buf[offset]; |
ashleymills | 0:5a29fd060ac8 | 376 | if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, |
ashleymills | 0:5a29fd060ac8 | 377 | len, PKCS12_KEY_ID)) < 0) |
ashleymills | 0:5a29fd060ac8 | 378 | goto error; |
ashleymills | 0:5a29fd060ac8 | 379 | |
ashleymills | 0:5a29fd060ac8 | 380 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 381 | |
ashleymills | 0:5a29fd060ac8 | 382 | /* load the private key */ |
ashleymills | 0:5a29fd060ac8 | 383 | if ((ret = p8_add_key(ssl_ctx, cert)) < 0) |
ashleymills | 0:5a29fd060ac8 | 384 | goto error; |
ashleymills | 0:5a29fd060ac8 | 385 | |
ashleymills | 0:5a29fd060ac8 | 386 | /* miss out on friendly name, local key id etc */ |
ashleymills | 0:5a29fd060ac8 | 387 | if (asn1_skip_obj(buf, &offset, ASN1_SET) < 0) |
ashleymills | 0:5a29fd060ac8 | 388 | goto error; |
ashleymills | 0:5a29fd060ac8 | 389 | |
ashleymills | 0:5a29fd060ac8 | 390 | /* work out the MAC */ |
ashleymills | 0:5a29fd060ac8 | 391 | if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 392 | asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 393 | asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 394 | (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || |
ashleymills | 0:5a29fd060ac8 | 395 | len != SHA1_SIZE) |
ashleymills | 0:5a29fd060ac8 | 396 | goto error; |
ashleymills | 0:5a29fd060ac8 | 397 | |
ashleymills | 0:5a29fd060ac8 | 398 | orig_mac = &buf[offset]; |
ashleymills | 0:5a29fd060ac8 | 399 | offset += len; |
ashleymills | 0:5a29fd060ac8 | 400 | |
ashleymills | 0:5a29fd060ac8 | 401 | /* get the salt */ |
ashleymills | 0:5a29fd060ac8 | 402 | if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || len != 8) |
ashleymills | 0:5a29fd060ac8 | 403 | goto error; |
ashleymills | 0:5a29fd060ac8 | 404 | |
ashleymills | 0:5a29fd060ac8 | 405 | salt = &buf[offset]; |
ashleymills | 0:5a29fd060ac8 | 406 | |
ashleymills | 0:5a29fd060ac8 | 407 | /* work out what the mac should be */ |
ashleymills | 0:5a29fd060ac8 | 408 | if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, |
ashleymills | 0:5a29fd060ac8 | 409 | key, SHA1_SIZE, PKCS12_MAC_ID)) < 0) |
ashleymills | 0:5a29fd060ac8 | 410 | goto error; |
ashleymills | 0:5a29fd060ac8 | 411 | |
ashleymills | 0:5a29fd060ac8 | 412 | hmac_sha1(auth_safes, auth_safes_len, key, SHA1_SIZE, mac); |
ashleymills | 0:5a29fd060ac8 | 413 | |
ashleymills | 0:5a29fd060ac8 | 414 | if (memcmp(mac, orig_mac, SHA1_SIZE)) |
ashleymills | 0:5a29fd060ac8 | 415 | { |
ashleymills | 0:5a29fd060ac8 | 416 | ret = SSL_ERROR_INVALID_HMAC; |
ashleymills | 0:5a29fd060ac8 | 417 | goto error; |
ashleymills | 0:5a29fd060ac8 | 418 | } |
ashleymills | 0:5a29fd060ac8 | 419 | |
ashleymills | 0:5a29fd060ac8 | 420 | error: |
ashleymills | 0:5a29fd060ac8 | 421 | free(version); |
ashleymills | 0:5a29fd060ac8 | 422 | free(uni_pass); |
ashleymills | 0:5a29fd060ac8 | 423 | free(auth_safes); |
ashleymills | 0:5a29fd060ac8 | 424 | return ret; |
ashleymills | 0:5a29fd060ac8 | 425 | } |
ashleymills | 0:5a29fd060ac8 | 426 | |
ashleymills | 0:5a29fd060ac8 | 427 | /* |
ashleymills | 0:5a29fd060ac8 | 428 | * Retrieve the salt/iteration details from a PBE block. |
ashleymills | 0:5a29fd060ac8 | 429 | */ |
ashleymills | 0:5a29fd060ac8 | 430 | static int get_pbe_params(uint8_t *buf, int *offset, |
ashleymills | 0:5a29fd060ac8 | 431 | const uint8_t **salt, int *iterations) |
ashleymills | 0:5a29fd060ac8 | 432 | { |
ashleymills | 0:5a29fd060ac8 | 433 | static const uint8_t pbeSH1RC4[] = /* pbeWithSHAAnd128BitRC4 */ |
ashleymills | 0:5a29fd060ac8 | 434 | { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01 }; |
ashleymills | 0:5a29fd060ac8 | 435 | |
ashleymills | 0:5a29fd060ac8 | 436 | int i, len; |
ashleymills | 0:5a29fd060ac8 | 437 | uint8_t *iter = NULL; |
ashleymills | 0:5a29fd060ac8 | 438 | int error_code = SSL_ERROR_NOT_SUPPORTED; |
ashleymills | 0:5a29fd060ac8 | 439 | |
ashleymills | 0:5a29fd060ac8 | 440 | /* Get the PBE type */ |
ashleymills | 0:5a29fd060ac8 | 441 | if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 442 | (len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) |
ashleymills | 0:5a29fd060ac8 | 443 | goto error; |
ashleymills | 0:5a29fd060ac8 | 444 | |
ashleymills | 0:5a29fd060ac8 | 445 | /* we expect pbeWithSHAAnd128BitRC4 (1.2.840.113549.1.12.1.1) |
ashleymills | 0:5a29fd060ac8 | 446 | which is the only algorithm we support */ |
ashleymills | 0:5a29fd060ac8 | 447 | if (len != sizeof(pbeSH1RC4) || |
ashleymills | 0:5a29fd060ac8 | 448 | memcmp(&buf[*offset], pbeSH1RC4, sizeof(pbeSH1RC4))) |
ashleymills | 0:5a29fd060ac8 | 449 | { |
ashleymills | 0:5a29fd060ac8 | 450 | #ifdef CONFIG_SSL_FULL_MODE |
ashleymills | 0:5a29fd060ac8 | 451 | printf("Error: pkcs8/pkcs12 must use \"PBE-SHA1-RC4-128\"\n"); |
ashleymills | 0:5a29fd060ac8 | 452 | #endif |
ashleymills | 0:5a29fd060ac8 | 453 | goto error; |
ashleymills | 0:5a29fd060ac8 | 454 | } |
ashleymills | 0:5a29fd060ac8 | 455 | |
ashleymills | 0:5a29fd060ac8 | 456 | *offset += len; |
ashleymills | 0:5a29fd060ac8 | 457 | |
ashleymills | 0:5a29fd060ac8 | 458 | if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || |
ashleymills | 0:5a29fd060ac8 | 459 | (len = asn1_next_obj(buf, offset, ASN1_OCTET_STRING)) < 0 || |
ashleymills | 0:5a29fd060ac8 | 460 | len != 8) |
ashleymills | 0:5a29fd060ac8 | 461 | goto error; |
ashleymills | 0:5a29fd060ac8 | 462 | |
ashleymills | 0:5a29fd060ac8 | 463 | *salt = &buf[*offset]; |
ashleymills | 0:5a29fd060ac8 | 464 | *offset += len; |
ashleymills | 0:5a29fd060ac8 | 465 | |
ashleymills | 0:5a29fd060ac8 | 466 | if ((len = asn1_get_int(buf, offset, &iter)) < 0) |
ashleymills | 0:5a29fd060ac8 | 467 | goto error; |
ashleymills | 0:5a29fd060ac8 | 468 | |
ashleymills | 0:5a29fd060ac8 | 469 | *iterations = 0; |
ashleymills | 0:5a29fd060ac8 | 470 | for (i = 0; i < len; i++) |
ashleymills | 0:5a29fd060ac8 | 471 | { |
ashleymills | 0:5a29fd060ac8 | 472 | (*iterations) <<= 8; |
ashleymills | 0:5a29fd060ac8 | 473 | (*iterations) += iter[i]; |
ashleymills | 0:5a29fd060ac8 | 474 | } |
ashleymills | 0:5a29fd060ac8 | 475 | |
ashleymills | 0:5a29fd060ac8 | 476 | free(iter); |
ashleymills | 0:5a29fd060ac8 | 477 | error_code = SSL_OK; /* got here - we are ok */ |
ashleymills | 0:5a29fd060ac8 | 478 | |
ashleymills | 0:5a29fd060ac8 | 479 | error: |
ashleymills | 0:5a29fd060ac8 | 480 | return error_code; |
ashleymills | 0:5a29fd060ac8 | 481 | } |
ashleymills | 0:5a29fd060ac8 | 482 | |
ashleymills | 0:5a29fd060ac8 | 483 | #endif |