diff options
| -rw-r--r-- | crypto/asymmetric_keys/.gitignore | 1 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/Kconfig | 10 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/Makefile | 17 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509.asn1 | 60 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_cert_parser.c | 497 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_parser.h | 36 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_public_key.c | 207 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_rsakey.asn1 | 4 |
8 files changed, 832 insertions, 0 deletions
diff --git a/crypto/asymmetric_keys/.gitignore b/crypto/asymmetric_keys/.gitignore new file mode 100644 index 000000000000..ee328374dba8 --- /dev/null +++ b/crypto/asymmetric_keys/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| *-asn1.[ch] | |||
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 561759d6a65f..6d2c2ea12559 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig | |||
| @@ -25,4 +25,14 @@ config PUBLIC_KEY_ALGO_RSA | |||
| 25 | help | 25 | help |
| 26 | This option enables support for the RSA algorithm (PKCS#1, RFC3447). | 26 | This option enables support for the RSA algorithm (PKCS#1, RFC3447). |
| 27 | 27 | ||
| 28 | config X509_CERTIFICATE_PARSER | ||
| 29 | tristate "X.509 certificate parser" | ||
| 30 | depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE | ||
| 31 | select ASN1 | ||
| 32 | select OID_REGISTRY | ||
| 33 | help | ||
| 34 | This option procides support for parsing X.509 format blobs for key | ||
| 35 | data and provides the ability to instantiate a crypto key from a | ||
| 36 | public key packet found inside the certificate. | ||
| 37 | |||
| 28 | endif # ASYMMETRIC_KEY_TYPE | 38 | endif # ASYMMETRIC_KEY_TYPE |
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 7c92691a45eb..0727204aab68 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile | |||
| @@ -8,3 +8,20 @@ asymmetric_keys-y := asymmetric_type.o signature.o | |||
| 8 | 8 | ||
| 9 | obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o | 9 | obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o |
| 10 | obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o | 10 | obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o |
| 11 | |||
| 12 | # | ||
| 13 | # X.509 Certificate handling | ||
| 14 | # | ||
| 15 | obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o | ||
| 16 | x509_key_parser-y := \ | ||
| 17 | x509-asn1.o \ | ||
| 18 | x509_rsakey-asn1.o \ | ||
| 19 | x509_cert_parser.o \ | ||
| 20 | x509_public_key.o | ||
| 21 | |||
| 22 | $(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h | ||
| 23 | $(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h | ||
| 24 | $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h | ||
| 25 | |||
| 26 | clean-files += x509-asn1.c x509-asn1.h | ||
| 27 | clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h | ||
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1 new file mode 100644 index 000000000000..bf32b3dff088 --- /dev/null +++ b/crypto/asymmetric_keys/x509.asn1 | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | Certificate ::= SEQUENCE { | ||
| 2 | tbsCertificate TBSCertificate ({ x509_note_tbs_certificate }), | ||
| 3 | signatureAlgorithm AlgorithmIdentifier, | ||
| 4 | signature BIT STRING ({ x509_note_signature }) | ||
| 5 | } | ||
| 6 | |||
| 7 | TBSCertificate ::= SEQUENCE { | ||
| 8 | version [ 0 ] Version DEFAULT, | ||
| 9 | serialNumber CertificateSerialNumber, | ||
| 10 | signature AlgorithmIdentifier ({ x509_note_pkey_algo }), | ||
| 11 | issuer Name ({ x509_note_issuer }), | ||
| 12 | validity Validity, | ||
| 13 | subject Name ({ x509_note_subject }), | ||
| 14 | subjectPublicKeyInfo SubjectPublicKeyInfo, | ||
| 15 | issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL, | ||
| 16 | subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL, | ||
| 17 | extensions [ 3 ] Extensions OPTIONAL | ||
| 18 | } | ||
| 19 | |||
| 20 | Version ::= INTEGER | ||
| 21 | CertificateSerialNumber ::= INTEGER | ||
| 22 | |||
| 23 | AlgorithmIdentifier ::= SEQUENCE { | ||
| 24 | algorithm OBJECT IDENTIFIER ({ x509_note_OID }), | ||
| 25 | parameters ANY OPTIONAL | ||
| 26 | } | ||
| 27 | |||
| 28 | Name ::= SEQUENCE OF RelativeDistinguishedName | ||
| 29 | |||
| 30 | RelativeDistinguishedName ::= SET OF AttributeValueAssertion | ||
| 31 | |||
| 32 | AttributeValueAssertion ::= SEQUENCE { | ||
| 33 | attributeType OBJECT IDENTIFIER ({ x509_note_OID }), | ||
| 34 | attributeValue ANY ({ x509_extract_name_segment }) | ||
| 35 | } | ||
| 36 | |||
| 37 | Validity ::= SEQUENCE { | ||
| 38 | notBefore Time ({ x509_note_not_before }), | ||
| 39 | notAfter Time ({ x509_note_not_after }) | ||
| 40 | } | ||
| 41 | |||
| 42 | Time ::= CHOICE { | ||
| 43 | utcTime UTCTime, | ||
| 44 | generalTime GeneralizedTime | ||
| 45 | } | ||
| 46 | |||
| 47 | SubjectPublicKeyInfo ::= SEQUENCE { | ||
| 48 | algorithm AlgorithmIdentifier, | ||
| 49 | subjectPublicKey BIT STRING ({ x509_extract_key_data }) | ||
| 50 | } | ||
| 51 | |||
| 52 | UniqueIdentifier ::= BIT STRING | ||
| 53 | |||
| 54 | Extensions ::= SEQUENCE OF Extension | ||
| 55 | |||
| 56 | Extension ::= SEQUENCE { | ||
| 57 | extnid OBJECT IDENTIFIER ({ x509_note_OID }), | ||
| 58 | critical BOOLEAN DEFAULT, | ||
| 59 | extnValue OCTET STRING ({ x509_process_extension }) | ||
| 60 | } | ||
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c new file mode 100644 index 000000000000..8fcac9493b7a --- /dev/null +++ b/crypto/asymmetric_keys/x509_cert_parser.c | |||
| @@ -0,0 +1,497 @@ | |||
| 1 | /* X.509 certificate parser | ||
| 2 | * | ||
| 3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public Licence | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the Licence, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #define pr_fmt(fmt) "X.509: "fmt | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | #include <linux/err.h> | ||
| 16 | #include <linux/oid_registry.h> | ||
| 17 | #include "public_key.h" | ||
| 18 | #include "x509_parser.h" | ||
| 19 | #include "x509-asn1.h" | ||
| 20 | #include "x509_rsakey-asn1.h" | ||
| 21 | |||
| 22 | struct x509_parse_context { | ||
| 23 | struct x509_certificate *cert; /* Certificate being constructed */ | ||
| 24 | unsigned long data; /* Start of data */ | ||
| 25 | const void *cert_start; /* Start of cert content */ | ||
| 26 | const void *key; /* Key data */ | ||
| 27 | size_t key_size; /* Size of key data */ | ||
| 28 | enum OID last_oid; /* Last OID encountered */ | ||
| 29 | enum OID algo_oid; /* Algorithm OID */ | ||
| 30 | unsigned char nr_mpi; /* Number of MPIs stored */ | ||
| 31 | u8 o_size; /* Size of organizationName (O) */ | ||
| 32 | u8 cn_size; /* Size of commonName (CN) */ | ||
| 33 | u8 email_size; /* Size of emailAddress */ | ||
| 34 | u16 o_offset; /* Offset of organizationName (O) */ | ||
| 35 | u16 cn_offset; /* Offset of commonName (CN) */ | ||
| 36 | u16 email_offset; /* Offset of emailAddress */ | ||
| 37 | }; | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Free an X.509 certificate | ||
| 41 | */ | ||
| 42 | void x509_free_certificate(struct x509_certificate *cert) | ||
| 43 | { | ||
| 44 | if (cert) { | ||
| 45 | public_key_destroy(cert->pub); | ||
| 46 | kfree(cert->issuer); | ||
| 47 | kfree(cert->subject); | ||
| 48 | kfree(cert->fingerprint); | ||
| 49 | kfree(cert->authority); | ||
| 50 | kfree(cert); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /* | ||
| 55 | * Parse an X.509 certificate | ||
| 56 | */ | ||
| 57 | struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) | ||
| 58 | { | ||
| 59 | struct x509_certificate *cert; | ||
| 60 | struct x509_parse_context *ctx; | ||
| 61 | long ret; | ||
| 62 | |||
| 63 | ret = -ENOMEM; | ||
| 64 | cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL); | ||
| 65 | if (!cert) | ||
| 66 | goto error_no_cert; | ||
| 67 | cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); | ||
| 68 | if (!cert->pub) | ||
| 69 | goto error_no_ctx; | ||
| 70 | ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); | ||
| 71 | if (!ctx) | ||
| 72 | goto error_no_ctx; | ||
| 73 | |||
| 74 | ctx->cert = cert; | ||
| 75 | ctx->data = (unsigned long)data; | ||
| 76 | |||
| 77 | /* Attempt to decode the certificate */ | ||
| 78 | ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen); | ||
| 79 | if (ret < 0) | ||
| 80 | goto error_decode; | ||
| 81 | |||
| 82 | /* Decode the public key */ | ||
| 83 | ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx, | ||
| 84 | ctx->key, ctx->key_size); | ||
| 85 | if (ret < 0) | ||
| 86 | goto error_decode; | ||
| 87 | |||
| 88 | kfree(ctx); | ||
| 89 | return cert; | ||
| 90 | |||
| 91 | error_decode: | ||
| 92 | kfree(ctx); | ||
| 93 | error_no_ctx: | ||
| 94 | x509_free_certificate(cert); | ||
| 95 | error_no_cert: | ||
| 96 | return ERR_PTR(ret); | ||
| 97 | } | ||
| 98 | |||
| 99 | /* | ||
| 100 | * Note an OID when we find one for later processing when we know how | ||
| 101 | * to interpret it. | ||
| 102 | */ | ||
| 103 | int x509_note_OID(void *context, size_t hdrlen, | ||
| 104 | unsigned char tag, | ||
| 105 | const void *value, size_t vlen) | ||
| 106 | { | ||
| 107 | struct x509_parse_context *ctx = context; | ||
| 108 | |||
| 109 | ctx->last_oid = look_up_OID(value, vlen); | ||
| 110 | if (ctx->last_oid == OID__NR) { | ||
| 111 | char buffer[50]; | ||
| 112 | sprint_oid(value, vlen, buffer, sizeof(buffer)); | ||
| 113 | pr_debug("Unknown OID: [%zu] %s\n", | ||
| 114 | (unsigned long)value - ctx->data, buffer); | ||
| 115 | } | ||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* | ||
| 120 | * Save the position of the TBS data so that we can check the signature over it | ||
| 121 | * later. | ||
| 122 | */ | ||
| 123 | int x509_note_tbs_certificate(void *context, size_t hdrlen, | ||
| 124 | unsigned char tag, | ||
| 125 | const void *value, size_t vlen) | ||
| 126 | { | ||
| 127 | struct x509_parse_context *ctx = context; | ||
| 128 | |||
| 129 | pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n", | ||
| 130 | hdrlen, tag, (unsigned long)value - ctx->data, vlen); | ||
| 131 | |||
| 132 | ctx->cert->tbs = value - hdrlen; | ||
| 133 | ctx->cert->tbs_size = vlen + hdrlen; | ||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* | ||
| 138 | * Record the public key algorithm | ||
| 139 | */ | ||
| 140 | int x509_note_pkey_algo(void *context, size_t hdrlen, | ||
| 141 | unsigned char tag, | ||
| 142 | const void *value, size_t vlen) | ||
| 143 | { | ||
| 144 | struct x509_parse_context *ctx = context; | ||
| 145 | |||
| 146 | pr_debug("PubKey Algo: %u\n", ctx->last_oid); | ||
| 147 | |||
| 148 | switch (ctx->last_oid) { | ||
| 149 | case OID_md2WithRSAEncryption: | ||
| 150 | case OID_md3WithRSAEncryption: | ||
| 151 | default: | ||
| 152 | return -ENOPKG; /* Unsupported combination */ | ||
| 153 | |||
| 154 | case OID_md4WithRSAEncryption: | ||
| 155 | ctx->cert->sig_hash_algo = PKEY_HASH_MD5; | ||
| 156 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
| 157 | break; | ||
| 158 | |||
| 159 | case OID_sha1WithRSAEncryption: | ||
| 160 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA1; | ||
| 161 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
| 162 | break; | ||
| 163 | |||
| 164 | case OID_sha256WithRSAEncryption: | ||
| 165 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA256; | ||
| 166 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
| 167 | break; | ||
| 168 | |||
| 169 | case OID_sha384WithRSAEncryption: | ||
| 170 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA384; | ||
| 171 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
| 172 | break; | ||
| 173 | |||
| 174 | case OID_sha512WithRSAEncryption: | ||
| 175 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA512; | ||
| 176 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
| 177 | break; | ||
| 178 | |||
| 179 | case OID_sha224WithRSAEncryption: | ||
| 180 | ctx->cert->sig_hash_algo = PKEY_HASH_SHA224; | ||
| 181 | ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | |||
| 185 | ctx->algo_oid = ctx->last_oid; | ||
| 186 | return 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | /* | ||
| 190 | * Note the whereabouts and type of the signature. | ||
| 191 | */ | ||
| 192 | int x509_note_signature(void *context, size_t hdrlen, | ||
| 193 | unsigned char tag, | ||
| 194 | const void *value, size_t vlen) | ||
| 195 | { | ||
| 196 | struct x509_parse_context *ctx = context; | ||
| 197 | |||
| 198 | pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen); | ||
| 199 | |||
| 200 | if (ctx->last_oid != ctx->algo_oid) { | ||
| 201 | pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n", | ||
| 202 | ctx->algo_oid, ctx->last_oid); | ||
| 203 | return -EINVAL; | ||
| 204 | } | ||
| 205 | |||
| 206 | ctx->cert->sig = value; | ||
| 207 | ctx->cert->sig_size = vlen; | ||
| 208 | return 0; | ||
| 209 | } | ||
| 210 | |||
| 211 | /* | ||
| 212 | * Note some of the name segments from which we'll fabricate a name. | ||
| 213 | */ | ||
| 214 | int x509_extract_name_segment(void *context, size_t hdrlen, | ||
| 215 | unsigned char tag, | ||
| 216 | const void *value, size_t vlen) | ||
| 217 | { | ||
| 218 | struct x509_parse_context *ctx = context; | ||
| 219 | |||
| 220 | switch (ctx->last_oid) { | ||
| 221 | case OID_commonName: | ||
| 222 | ctx->cn_size = vlen; | ||
| 223 | ctx->cn_offset = (unsigned long)value - ctx->data; | ||
| 224 | break; | ||
| 225 | case OID_organizationName: | ||
| 226 | ctx->o_size = vlen; | ||
| 227 | ctx->o_offset = (unsigned long)value - ctx->data; | ||
| 228 | break; | ||
| 229 | case OID_email_address: | ||
| 230 | ctx->email_size = vlen; | ||
| 231 | ctx->email_offset = (unsigned long)value - ctx->data; | ||
| 232 | break; | ||
| 233 | default: | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | |||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* | ||
| 241 | * Fabricate and save the issuer and subject names | ||
| 242 | */ | ||
| 243 | static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen, | ||
| 244 | unsigned char tag, | ||
| 245 | char **_name, size_t vlen) | ||
| 246 | { | ||
| 247 | const void *name, *data = (const void *)ctx->data; | ||
| 248 | size_t namesize; | ||
| 249 | char *buffer; | ||
| 250 | |||
| 251 | if (*_name) | ||
| 252 | return -EINVAL; | ||
| 253 | |||
| 254 | /* Empty name string if no material */ | ||
| 255 | if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) { | ||
| 256 | buffer = kmalloc(1, GFP_KERNEL); | ||
| 257 | if (!buffer) | ||
| 258 | return -ENOMEM; | ||
| 259 | buffer[0] = 0; | ||
| 260 | goto done; | ||
| 261 | } | ||
| 262 | |||
| 263 | if (ctx->cn_size && ctx->o_size) { | ||
| 264 | /* Consider combining O and CN, but use only the CN if it is | ||
| 265 | * prefixed by the O, or a significant portion thereof. | ||
| 266 | */ | ||
| 267 | namesize = ctx->cn_size; | ||
| 268 | name = data + ctx->cn_offset; | ||
| 269 | if (ctx->cn_size >= ctx->o_size && | ||
| 270 | memcmp(data + ctx->cn_offset, data + ctx->o_offset, | ||
| 271 | ctx->o_size) == 0) | ||
| 272 | goto single_component; | ||
| 273 | if (ctx->cn_size >= 7 && | ||
| 274 | ctx->o_size >= 7 && | ||
| 275 | memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0) | ||
| 276 | goto single_component; | ||
| 277 | |||
| 278 | buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1, | ||
| 279 | GFP_KERNEL); | ||
| 280 | if (!buffer) | ||
| 281 | return -ENOMEM; | ||
| 282 | |||
| 283 | memcpy(buffer, | ||
| 284 | data + ctx->o_offset, ctx->o_size); | ||
| 285 | buffer[ctx->o_size + 0] = ':'; | ||
| 286 | buffer[ctx->o_size + 1] = ' '; | ||
| 287 | memcpy(buffer + ctx->o_size + 2, | ||
| 288 | data + ctx->cn_offset, ctx->cn_size); | ||
| 289 | buffer[ctx->o_size + 2 + ctx->cn_size] = 0; | ||
| 290 | goto done; | ||
| 291 | |||
| 292 | } else if (ctx->cn_size) { | ||
| 293 | namesize = ctx->cn_size; | ||
| 294 | name = data + ctx->cn_offset; | ||
| 295 | } else if (ctx->o_size) { | ||
| 296 | namesize = ctx->o_size; | ||
| 297 | name = data + ctx->o_offset; | ||
| 298 | } else { | ||
| 299 | namesize = ctx->email_size; | ||
| 300 | name = data + ctx->email_offset; | ||
| 301 | } | ||
| 302 | |||
| 303 | single_component: | ||
| 304 | buffer = kmalloc(namesize + 1, GFP_KERNEL); | ||
| 305 | if (!buffer) | ||
| 306 | return -ENOMEM; | ||
| 307 | memcpy(buffer, name, namesize); | ||
| 308 | buffer[namesize] = 0; | ||
| 309 | |||
| 310 | done: | ||
| 311 | *_name = buffer; | ||
| 312 | ctx->cn_size = 0; | ||
| 313 | ctx->o_size = 0; | ||
| 314 | ctx->email_size = 0; | ||
| 315 | return 0; | ||
| 316 | } | ||
| 317 | |||
| 318 | int x509_note_issuer(void *context, size_t hdrlen, | ||
| 319 | unsigned char tag, | ||
| 320 | const void *value, size_t vlen) | ||
| 321 | { | ||
| 322 | struct x509_parse_context *ctx = context; | ||
| 323 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); | ||
| 324 | } | ||
| 325 | |||
| 326 | int x509_note_subject(void *context, size_t hdrlen, | ||
| 327 | unsigned char tag, | ||
| 328 | const void *value, size_t vlen) | ||
| 329 | { | ||
| 330 | struct x509_parse_context *ctx = context; | ||
| 331 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); | ||
| 332 | } | ||
| 333 | |||
| 334 | /* | ||
| 335 | * Extract the data for the public key algorithm | ||
| 336 | */ | ||
| 337 | int x509_extract_key_data(void *context, size_t hdrlen, | ||
| 338 | unsigned char tag, | ||
| 339 | const void *value, size_t vlen) | ||
| 340 | { | ||
| 341 | struct x509_parse_context *ctx = context; | ||
| 342 | |||
| 343 | if (ctx->last_oid != OID_rsaEncryption) | ||
| 344 | return -ENOPKG; | ||
| 345 | |||
| 346 | /* There seems to be an extraneous 0 byte on the front of the data */ | ||
| 347 | ctx->cert->pkey_algo = PKEY_ALGO_RSA; | ||
| 348 | ctx->key = value + 1; | ||
| 349 | ctx->key_size = vlen - 1; | ||
| 350 | return 0; | ||
| 351 | } | ||
| 352 | |||
| 353 | /* | ||
| 354 | * Extract a RSA public key value | ||
| 355 | */ | ||
| 356 | int rsa_extract_mpi(void *context, size_t hdrlen, | ||
| 357 | unsigned char tag, | ||
| 358 | const void *value, size_t vlen) | ||
| 359 | { | ||
| 360 | struct x509_parse_context *ctx = context; | ||
| 361 | MPI mpi; | ||
| 362 | |||
| 363 | if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) { | ||
| 364 | pr_err("Too many public key MPIs in certificate\n"); | ||
| 365 | return -EBADMSG; | ||
| 366 | } | ||
| 367 | |||
| 368 | mpi = mpi_read_raw_data(value, vlen); | ||
| 369 | if (!mpi) | ||
| 370 | return -ENOMEM; | ||
| 371 | |||
| 372 | ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi; | ||
| 373 | return 0; | ||
| 374 | } | ||
| 375 | |||
| 376 | /* | ||
| 377 | * Process certificate extensions that are used to qualify the certificate. | ||
| 378 | */ | ||
| 379 | int x509_process_extension(void *context, size_t hdrlen, | ||
| 380 | unsigned char tag, | ||
| 381 | const void *value, size_t vlen) | ||
| 382 | { | ||
| 383 | struct x509_parse_context *ctx = context; | ||
| 384 | const unsigned char *v = value; | ||
| 385 | char *f; | ||
| 386 | int i; | ||
| 387 | |||
| 388 | pr_debug("Extension: %u\n", ctx->last_oid); | ||
| 389 | |||
| 390 | if (ctx->last_oid == OID_subjectKeyIdentifier) { | ||
| 391 | /* Get hold of the key fingerprint */ | ||
| 392 | if (vlen < 3) | ||
| 393 | return -EBADMSG; | ||
| 394 | if (v[0] != ASN1_OTS || v[1] != vlen - 2) | ||
| 395 | return -EBADMSG; | ||
| 396 | v += 2; | ||
| 397 | vlen -= 2; | ||
| 398 | |||
| 399 | f = kmalloc(vlen * 2 + 1, GFP_KERNEL); | ||
| 400 | if (!f) | ||
| 401 | return -ENOMEM; | ||
| 402 | for (i = 0; i < vlen; i++) | ||
| 403 | sprintf(f + i * 2, "%02x", v[i]); | ||
| 404 | pr_debug("fingerprint %s\n", f); | ||
| 405 | ctx->cert->fingerprint = f; | ||
| 406 | return 0; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (ctx->last_oid == OID_authorityKeyIdentifier) { | ||
| 410 | /* Get hold of the CA key fingerprint */ | ||
| 411 | if (vlen < 5) | ||
| 412 | return -EBADMSG; | ||
| 413 | if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)) || | ||
| 414 | v[1] != vlen - 2 || | ||
| 415 | v[2] != (ASN1_CONT << 6) || | ||
| 416 | v[3] != vlen - 4) | ||
| 417 | return -EBADMSG; | ||
| 418 | v += 4; | ||
| 419 | vlen -= 4; | ||
| 420 | |||
| 421 | f = kmalloc(vlen * 2 + 1, GFP_KERNEL); | ||
| 422 | if (!f) | ||
| 423 | return -ENOMEM; | ||
| 424 | for (i = 0; i < vlen; i++) | ||
| 425 | sprintf(f + i * 2, "%02x", v[i]); | ||
| 426 | pr_debug("authority %s\n", f); | ||
| 427 | ctx->cert->authority = f; | ||
| 428 | return 0; | ||
| 429 | } | ||
| 430 | |||
| 431 | return 0; | ||
| 432 | } | ||
| 433 | |||
| 434 | /* | ||
| 435 | * Record a certificate time. | ||
| 436 | */ | ||
| 437 | static int x509_note_time(time_t *_time, size_t hdrlen, | ||
| 438 | unsigned char tag, | ||
| 439 | const unsigned char *value, size_t vlen) | ||
| 440 | { | ||
| 441 | unsigned YY, MM, DD, hh, mm, ss; | ||
| 442 | const unsigned char *p = value; | ||
| 443 | |||
| 444 | #define dec2bin(X) ((X) - '0') | ||
| 445 | #define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; }) | ||
| 446 | |||
| 447 | if (tag == ASN1_UNITIM) { | ||
| 448 | /* UTCTime: YYMMDDHHMMSSZ */ | ||
| 449 | if (vlen != 13) | ||
| 450 | goto unsupported_time; | ||
| 451 | YY = DD2bin(p); | ||
| 452 | if (YY > 50) | ||
| 453 | YY += 1900; | ||
| 454 | else | ||
| 455 | YY += 2000; | ||
| 456 | } else if (tag == ASN1_GENTIM) { | ||
| 457 | /* GenTime: YYYYMMDDHHMMSSZ */ | ||
| 458 | if (vlen != 15) | ||
| 459 | goto unsupported_time; | ||
| 460 | YY = DD2bin(p) * 100 + DD2bin(p); | ||
| 461 | } else { | ||
| 462 | goto unsupported_time; | ||
| 463 | } | ||
| 464 | |||
| 465 | MM = DD2bin(p); | ||
| 466 | DD = DD2bin(p); | ||
| 467 | hh = DD2bin(p); | ||
| 468 | mm = DD2bin(p); | ||
| 469 | ss = DD2bin(p); | ||
| 470 | |||
| 471 | if (*p != 'Z') | ||
| 472 | goto unsupported_time; | ||
| 473 | |||
| 474 | *_time = mktime(YY, MM, DD, hh, mm, ss); | ||
| 475 | return 0; | ||
| 476 | |||
| 477 | unsupported_time: | ||
| 478 | pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n", | ||
| 479 | tag, (int)vlen, (int)vlen, value); | ||
| 480 | return -EBADMSG; | ||
| 481 | } | ||
| 482 | |||
| 483 | int x509_note_not_before(void *context, size_t hdrlen, | ||
| 484 | unsigned char tag, | ||
| 485 | const void *value, size_t vlen) | ||
| 486 | { | ||
| 487 | struct x509_parse_context *ctx = context; | ||
| 488 | return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen); | ||
| 489 | } | ||
| 490 | |||
| 491 | int x509_note_not_after(void *context, size_t hdrlen, | ||
| 492 | unsigned char tag, | ||
| 493 | const void *value, size_t vlen) | ||
| 494 | { | ||
| 495 | struct x509_parse_context *ctx = context; | ||
| 496 | return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen); | ||
| 497 | } | ||
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h new file mode 100644 index 000000000000..635053f7e962 --- /dev/null +++ b/crypto/asymmetric_keys/x509_parser.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* X.509 certificate parser internal definitions | ||
| 2 | * | ||
| 3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public Licence | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the Licence, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <crypto/public_key.h> | ||
| 13 | |||
| 14 | struct x509_certificate { | ||
| 15 | struct x509_certificate *next; | ||
| 16 | struct public_key *pub; /* Public key details */ | ||
| 17 | char *issuer; /* Name of certificate issuer */ | ||
| 18 | char *subject; /* Name of certificate subject */ | ||
| 19 | char *fingerprint; /* Key fingerprint as hex */ | ||
| 20 | char *authority; /* Authority key fingerprint as hex */ | ||
| 21 | time_t valid_from; | ||
| 22 | time_t valid_to; | ||
| 23 | enum pkey_algo pkey_algo : 8; /* Public key algorithm */ | ||
| 24 | enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */ | ||
| 25 | enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */ | ||
| 26 | const void *tbs; /* Signed data */ | ||
| 27 | size_t tbs_size; /* Size of signed data */ | ||
| 28 | const void *sig; /* Signature data */ | ||
| 29 | size_t sig_size; /* Size of sigature */ | ||
| 30 | }; | ||
| 31 | |||
| 32 | /* | ||
| 33 | * x509_cert_parser.c | ||
| 34 | */ | ||
| 35 | extern void x509_free_certificate(struct x509_certificate *cert); | ||
| 36 | extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); | ||
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c new file mode 100644 index 000000000000..716917ce0907 --- /dev/null +++ b/crypto/asymmetric_keys/x509_public_key.c | |||
| @@ -0,0 +1,207 @@ | |||
| 1 | /* Instantiate a public key crypto key from an X.509 Certificate | ||
| 2 | * | ||
| 3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public Licence | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the Licence, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #define pr_fmt(fmt) "X.509: "fmt | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/slab.h> | ||
| 16 | #include <linux/err.h> | ||
| 17 | #include <linux/mpi.h> | ||
| 18 | #include <linux/asn1_decoder.h> | ||
| 19 | #include <keys/asymmetric-subtype.h> | ||
| 20 | #include <keys/asymmetric-parser.h> | ||
| 21 | #include <crypto/hash.h> | ||
| 22 | #include "asymmetric_keys.h" | ||
| 23 | #include "public_key.h" | ||
| 24 | #include "x509_parser.h" | ||
| 25 | |||
| 26 | static const | ||
| 27 | struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = { | ||
| 28 | [PKEY_ALGO_DSA] = NULL, | ||
| 29 | #if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \ | ||
| 30 | defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE) | ||
| 31 | [PKEY_ALGO_RSA] = &RSA_public_key_algorithm, | ||
| 32 | #endif | ||
| 33 | }; | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Check the signature on a certificate using the provided public key | ||
| 37 | */ | ||
| 38 | static int x509_check_signature(const struct public_key *pub, | ||
| 39 | const struct x509_certificate *cert) | ||
| 40 | { | ||
| 41 | struct public_key_signature *sig; | ||
| 42 | struct crypto_shash *tfm; | ||
| 43 | struct shash_desc *desc; | ||
| 44 | size_t digest_size, desc_size; | ||
| 45 | int ret; | ||
| 46 | |||
| 47 | pr_devel("==>%s()\n", __func__); | ||
| 48 | |||
| 49 | /* Allocate the hashing algorithm we're going to need and find out how | ||
| 50 | * big the hash operational data will be. | ||
| 51 | */ | ||
| 52 | tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0); | ||
| 53 | if (IS_ERR(tfm)) | ||
| 54 | return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); | ||
| 55 | |||
| 56 | desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); | ||
| 57 | digest_size = crypto_shash_digestsize(tfm); | ||
| 58 | |||
| 59 | /* We allocate the hash operational data storage on the end of our | ||
| 60 | * context data. | ||
| 61 | */ | ||
| 62 | ret = -ENOMEM; | ||
| 63 | sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL); | ||
| 64 | if (!sig) | ||
| 65 | goto error_no_sig; | ||
| 66 | |||
| 67 | sig->pkey_hash_algo = cert->sig_hash_algo; | ||
| 68 | sig->digest = (u8 *)sig + sizeof(*sig) + desc_size; | ||
| 69 | sig->digest_size = digest_size; | ||
| 70 | |||
| 71 | desc = (void *)sig + sizeof(*sig); | ||
| 72 | desc->tfm = tfm; | ||
| 73 | desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; | ||
| 74 | |||
| 75 | ret = crypto_shash_init(desc); | ||
| 76 | if (ret < 0) | ||
| 77 | goto error; | ||
| 78 | |||
| 79 | ret = -ENOMEM; | ||
| 80 | sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size); | ||
| 81 | if (!sig->rsa.s) | ||
| 82 | goto error; | ||
| 83 | |||
| 84 | ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); | ||
| 85 | if (ret < 0) | ||
| 86 | goto error_mpi; | ||
| 87 | |||
| 88 | ret = pub->algo->verify_signature(pub, sig); | ||
| 89 | |||
| 90 | pr_debug("Cert Verification: %d\n", ret); | ||
| 91 | |||
| 92 | error_mpi: | ||
| 93 | mpi_free(sig->rsa.s); | ||
| 94 | error: | ||
| 95 | kfree(sig); | ||
| 96 | error_no_sig: | ||
| 97 | crypto_free_shash(tfm); | ||
| 98 | |||
| 99 | pr_devel("<==%s() = %d\n", __func__, ret); | ||
| 100 | return ret; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* | ||
| 104 | * Attempt to parse a data blob for a key as an X509 certificate. | ||
| 105 | */ | ||
| 106 | static int x509_key_preparse(struct key_preparsed_payload *prep) | ||
| 107 | { | ||
| 108 | struct x509_certificate *cert; | ||
| 109 | time_t now; | ||
| 110 | size_t srlen, sulen; | ||
| 111 | char *desc = NULL; | ||
| 112 | int ret; | ||
| 113 | |||
| 114 | cert = x509_cert_parse(prep->data, prep->datalen); | ||
| 115 | if (IS_ERR(cert)) | ||
| 116 | return PTR_ERR(cert); | ||
| 117 | |||
| 118 | pr_devel("Cert Issuer: %s\n", cert->issuer); | ||
| 119 | pr_devel("Cert Subject: %s\n", cert->subject); | ||
| 120 | pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]); | ||
| 121 | pr_devel("Cert Valid: %lu - %lu\n", cert->valid_from, cert->valid_to); | ||
| 122 | pr_devel("Cert Signature: %s + %s\n", | ||
| 123 | pkey_algo[cert->sig_pkey_algo], | ||
| 124 | pkey_hash_algo[cert->sig_hash_algo]); | ||
| 125 | |||
| 126 | if (!cert->fingerprint || !cert->authority) { | ||
| 127 | pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n", | ||
| 128 | cert->subject); | ||
| 129 | ret = -EKEYREJECTED; | ||
| 130 | goto error_free_cert; | ||
| 131 | } | ||
| 132 | |||
| 133 | now = CURRENT_TIME.tv_sec; | ||
| 134 | if (now < cert->valid_from) { | ||
| 135 | pr_warn("Cert %s is not yet valid\n", cert->fingerprint); | ||
| 136 | ret = -EKEYREJECTED; | ||
| 137 | goto error_free_cert; | ||
| 138 | } | ||
| 139 | if (now >= cert->valid_to) { | ||
| 140 | pr_warn("Cert %s has expired\n", cert->fingerprint); | ||
| 141 | ret = -EKEYEXPIRED; | ||
| 142 | goto error_free_cert; | ||
| 143 | } | ||
| 144 | |||
| 145 | cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo]; | ||
| 146 | cert->pub->id_type = PKEY_ID_X509; | ||
| 147 | |||
| 148 | /* Check the signature on the key */ | ||
| 149 | if (strcmp(cert->fingerprint, cert->authority) == 0) { | ||
| 150 | ret = x509_check_signature(cert->pub, cert); | ||
| 151 | if (ret < 0) | ||
| 152 | goto error_free_cert; | ||
| 153 | } | ||
| 154 | |||
| 155 | /* Propose a description */ | ||
| 156 | sulen = strlen(cert->subject); | ||
| 157 | srlen = strlen(cert->fingerprint); | ||
| 158 | ret = -ENOMEM; | ||
| 159 | desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); | ||
| 160 | if (!desc) | ||
| 161 | goto error_free_cert; | ||
| 162 | memcpy(desc, cert->subject, sulen); | ||
| 163 | desc[sulen] = ':'; | ||
| 164 | desc[sulen + 1] = ' '; | ||
| 165 | memcpy(desc + sulen + 2, cert->fingerprint, srlen); | ||
| 166 | desc[sulen + 2 + srlen] = 0; | ||
| 167 | |||
| 168 | /* We're pinning the module by being linked against it */ | ||
| 169 | __module_get(public_key_subtype.owner); | ||
| 170 | prep->type_data[0] = &public_key_subtype; | ||
| 171 | prep->type_data[1] = cert->fingerprint; | ||
| 172 | prep->payload = cert->pub; | ||
| 173 | prep->description = desc; | ||
| 174 | prep->quotalen = 100; | ||
| 175 | |||
| 176 | /* We've finished with the certificate */ | ||
| 177 | cert->pub = NULL; | ||
| 178 | cert->fingerprint = NULL; | ||
| 179 | desc = NULL; | ||
| 180 | ret = 0; | ||
| 181 | |||
| 182 | error_free_cert: | ||
| 183 | x509_free_certificate(cert); | ||
| 184 | return ret; | ||
| 185 | } | ||
| 186 | |||
| 187 | static struct asymmetric_key_parser x509_key_parser = { | ||
| 188 | .owner = THIS_MODULE, | ||
| 189 | .name = "x509", | ||
| 190 | .parse = x509_key_preparse, | ||
| 191 | }; | ||
| 192 | |||
| 193 | /* | ||
| 194 | * Module stuff | ||
| 195 | */ | ||
| 196 | static int __init x509_key_init(void) | ||
| 197 | { | ||
| 198 | return register_asymmetric_key_parser(&x509_key_parser); | ||
| 199 | } | ||
| 200 | |||
| 201 | static void __exit x509_key_exit(void) | ||
| 202 | { | ||
| 203 | unregister_asymmetric_key_parser(&x509_key_parser); | ||
| 204 | } | ||
| 205 | |||
| 206 | module_init(x509_key_init); | ||
| 207 | module_exit(x509_key_exit); | ||
diff --git a/crypto/asymmetric_keys/x509_rsakey.asn1 b/crypto/asymmetric_keys/x509_rsakey.asn1 new file mode 100644 index 000000000000..4ec7cc6532c1 --- /dev/null +++ b/crypto/asymmetric_keys/x509_rsakey.asn1 | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | RSAPublicKey ::= SEQUENCE { | ||
| 2 | modulus INTEGER ({ rsa_extract_mpi }), -- n | ||
| 3 | publicExponent INTEGER ({ rsa_extract_mpi }) -- e | ||
| 4 | } | ||
