Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 1 | /* |
| 2 | * PKCS #8 (Private-key information syntax) |
| 3 | * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> |
| 4 | * |
Dmitry Shmidt | c5ec7f5 | 2012-03-06 16:33:24 -0800 | [diff] [blame^] | 5 | * This software may be distributed under the terms of the BSD license. |
| 6 | * See README for more details. |
Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 7 | */ |
| 8 | |
| 9 | #include "includes.h" |
| 10 | |
| 11 | #include "common.h" |
| 12 | #include "asn1.h" |
| 13 | #include "bignum.h" |
| 14 | #include "rsa.h" |
| 15 | #include "pkcs5.h" |
| 16 | #include "pkcs8.h" |
| 17 | |
| 18 | |
| 19 | struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len) |
| 20 | { |
| 21 | struct asn1_hdr hdr; |
| 22 | const u8 *pos, *end; |
| 23 | struct bignum *zero; |
| 24 | struct asn1_oid oid; |
| 25 | char obuf[80]; |
| 26 | |
| 27 | /* PKCS #8, Chapter 6 */ |
| 28 | |
| 29 | /* PrivateKeyInfo ::= SEQUENCE */ |
| 30 | if (asn1_get_next(buf, len, &hdr) < 0 || |
| 31 | hdr.class != ASN1_CLASS_UNIVERSAL || |
| 32 | hdr.tag != ASN1_TAG_SEQUENCE) { |
| 33 | wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " |
| 34 | "header (SEQUENCE); assume PKCS #8 not used"); |
| 35 | return NULL; |
| 36 | } |
| 37 | pos = hdr.payload; |
| 38 | end = pos + hdr.length; |
| 39 | |
| 40 | /* version Version (Version ::= INTEGER) */ |
| 41 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || |
| 42 | hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { |
| 43 | wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found " |
| 44 | "class %d tag 0x%x; assume PKCS #8 not used", |
| 45 | hdr.class, hdr.tag); |
| 46 | return NULL; |
| 47 | } |
| 48 | |
| 49 | zero = bignum_init(); |
| 50 | if (zero == NULL) |
| 51 | return NULL; |
| 52 | |
| 53 | if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) { |
| 54 | wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER"); |
| 55 | bignum_deinit(zero); |
| 56 | return NULL; |
| 57 | } |
| 58 | pos = hdr.payload + hdr.length; |
| 59 | |
| 60 | if (bignum_cmp_d(zero, 0) != 0) { |
| 61 | wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the " |
| 62 | "beginning of private key; not found; assume " |
| 63 | "PKCS #8 not used"); |
| 64 | bignum_deinit(zero); |
| 65 | return NULL; |
| 66 | } |
| 67 | bignum_deinit(zero); |
| 68 | |
| 69 | /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier |
| 70 | * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */ |
| 71 | if (asn1_get_next(pos, len, &hdr) < 0 || |
| 72 | hdr.class != ASN1_CLASS_UNIVERSAL || |
| 73 | hdr.tag != ASN1_TAG_SEQUENCE) { |
| 74 | wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " |
| 75 | "(AlgorithmIdentifier) - found class %d tag 0x%x; " |
| 76 | "assume PKCS #8 not used", |
| 77 | hdr.class, hdr.tag); |
| 78 | return NULL; |
| 79 | } |
| 80 | |
| 81 | if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) { |
| 82 | wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID " |
| 83 | "(algorithm); assume PKCS #8 not used"); |
| 84 | return NULL; |
| 85 | } |
| 86 | |
| 87 | asn1_oid_to_str(&oid, obuf, sizeof(obuf)); |
| 88 | wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf); |
| 89 | |
| 90 | if (oid.len != 7 || |
| 91 | oid.oid[0] != 1 /* iso */ || |
| 92 | oid.oid[1] != 2 /* member-body */ || |
| 93 | oid.oid[2] != 840 /* us */ || |
| 94 | oid.oid[3] != 113549 /* rsadsi */ || |
| 95 | oid.oid[4] != 1 /* pkcs */ || |
| 96 | oid.oid[5] != 1 /* pkcs-1 */ || |
| 97 | oid.oid[6] != 1 /* rsaEncryption */) { |
| 98 | wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key " |
| 99 | "algorithm %s", obuf); |
| 100 | return NULL; |
| 101 | } |
| 102 | |
| 103 | pos = hdr.payload + hdr.length; |
| 104 | |
| 105 | /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */ |
| 106 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || |
| 107 | hdr.class != ASN1_CLASS_UNIVERSAL || |
| 108 | hdr.tag != ASN1_TAG_OCTETSTRING) { |
| 109 | wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " |
| 110 | "(privateKey) - found class %d tag 0x%x", |
| 111 | hdr.class, hdr.tag); |
| 112 | return NULL; |
| 113 | } |
| 114 | wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey"); |
| 115 | |
| 116 | return (struct crypto_private_key *) |
| 117 | crypto_rsa_import_private_key(hdr.payload, hdr.length); |
| 118 | } |
| 119 | |
| 120 | |
| 121 | struct crypto_private_key * |
| 122 | pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd) |
| 123 | { |
| 124 | struct asn1_hdr hdr; |
| 125 | const u8 *pos, *end, *enc_alg; |
| 126 | size_t enc_alg_len; |
| 127 | u8 *data; |
| 128 | size_t data_len; |
| 129 | |
| 130 | if (passwd == NULL) |
| 131 | return NULL; |
| 132 | |
| 133 | /* |
| 134 | * PKCS #8, Chapter 7 |
| 135 | * EncryptedPrivateKeyInfo ::= SEQUENCE { |
| 136 | * encryptionAlgorithm EncryptionAlgorithmIdentifier, |
| 137 | * encryptedData EncryptedData } |
| 138 | * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier |
| 139 | * EncryptedData ::= OCTET STRING |
| 140 | */ |
| 141 | |
| 142 | if (asn1_get_next(buf, len, &hdr) < 0 || |
| 143 | hdr.class != ASN1_CLASS_UNIVERSAL || |
| 144 | hdr.tag != ASN1_TAG_SEQUENCE) { |
| 145 | wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " |
| 146 | "header (SEQUENCE); assume encrypted PKCS #8 not " |
| 147 | "used"); |
| 148 | return NULL; |
| 149 | } |
| 150 | pos = hdr.payload; |
| 151 | end = pos + hdr.length; |
| 152 | |
| 153 | /* encryptionAlgorithm EncryptionAlgorithmIdentifier */ |
| 154 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || |
| 155 | hdr.class != ASN1_CLASS_UNIVERSAL || |
| 156 | hdr.tag != ASN1_TAG_SEQUENCE) { |
| 157 | wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " |
| 158 | "(AlgorithmIdentifier) - found class %d tag 0x%x; " |
| 159 | "assume encrypted PKCS #8 not used", |
| 160 | hdr.class, hdr.tag); |
| 161 | return NULL; |
| 162 | } |
| 163 | enc_alg = hdr.payload; |
| 164 | enc_alg_len = hdr.length; |
| 165 | pos = hdr.payload + hdr.length; |
| 166 | |
| 167 | /* encryptedData EncryptedData */ |
| 168 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || |
| 169 | hdr.class != ASN1_CLASS_UNIVERSAL || |
| 170 | hdr.tag != ASN1_TAG_OCTETSTRING) { |
| 171 | wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " |
| 172 | "(encryptedData) - found class %d tag 0x%x", |
| 173 | hdr.class, hdr.tag); |
| 174 | return NULL; |
| 175 | } |
| 176 | |
| 177 | data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, |
| 178 | passwd, &data_len); |
| 179 | if (data) { |
| 180 | struct crypto_private_key *key; |
| 181 | key = pkcs8_key_import(data, data_len); |
| 182 | os_free(data); |
| 183 | return key; |
| 184 | } |
| 185 | |
| 186 | return NULL; |
| 187 | } |