diff options
Diffstat (limited to 'chip/g/dcrypto/x509.c')
-rw-r--r-- | chip/g/dcrypto/x509.c | 545 |
1 files changed, 0 insertions, 545 deletions
diff --git a/chip/g/dcrypto/x509.c b/chip/g/dcrypto/x509.c deleted file mode 100644 index 81f1674db1..0000000000 --- a/chip/g/dcrypto/x509.c +++ /dev/null @@ -1,545 +0,0 @@ -/* Copyright 2016 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "dcrypto.h" - -#include <stdint.h> - -/* Limit the size of long form encoded objects to < 64 kB. */ -#define MAX_ASN1_OBJ_LEN_BYTES 3 - -/* Reserve space for TLV encoding */ -#define SEQ_SMALL 2 /* < 128 bytes (1B type, 1B 7-bit length) */ -#define SEQ_MEDIUM 3 /* < 256 bytes (1B type, 1B length size, 1B length) */ -#define SEQ_LARGE 4 /* < 65536 bytes (1B type, 1B length size, 2B length) */ - -/* Tag related constants. */ -enum { - V_ASN1_INT = 0x02, - V_ASN1_BIT_STRING = 0x03, - V_ASN1_BYTES = 0x04, - V_ASN1_OBJ = 0x06, - V_ASN1_UTF8 = 0x0c, - V_ASN1_SEQUENCE = 0x10, - V_ASN1_SET = 0x11, - V_ASN1_ASCII = 0x13, - V_ASN1_TIME = 0x18, - V_ASN1_CONSTRUCTED = 0x20, - /* short helpers */ - V_BITS = V_ASN1_BIT_STRING, - V_SEQ = V_ASN1_CONSTRUCTED | V_ASN1_SEQUENCE, - V_SET = V_ASN1_CONSTRUCTED | V_ASN1_SET, -}; - -struct asn1 { - uint8_t *p; - size_t n; -}; - - -#define SEQ_START(X, T, L) \ - do { \ - int __old = (X).n; \ - uint8_t __t = (T); \ - int __l = (L); \ - (X).n += __l; -#define SEQ_END(X) \ - (X).n = asn1_seq((X).p + __old, __t, __l, (X).n - __old - __l) + __old;\ - } \ - while (0) - -/* The SHA256 OID, from https://tools.ietf.org/html/rfc5754#section-3.2 - * Only the object bytes below, the DER encoding header ([0x30 0x0d]) - * is verified by the parser. */ -static const uint8_t OID_SHA256_WITH_RSA_ENCRYPTION[13] = { - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x0b, 0x05, 0x00 -}; -static const uint8_t OID_commonName[3] = {0x55, 0x04, 0x03}; -static const uint8_t OID_ecdsa_with_SHA256[8] = {0x2A, 0x86, 0x48, 0xCE, - 0x3D, 0x04, 0x03, 0x02}; -static const uint8_t OID_id_ecPublicKey[7] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, - 0x02, 0x01}; -static const uint8_t OID_prime256v1[8] = {0x2A, 0x86, 0x48, 0xCE, - 0x3D, 0x03, 0x01, 0x07}; -static const uint8_t OID_fido_u2f[11] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, - 0xE5, 0x1C, 0x02, 0x01, 0x01}; -#define OID(X) sizeof(OID_##X), OID_##X - -/* ---- ASN.1 Generation ---- */ - -/* start a tag and return write ptr */ -static uint8_t *asn1_tag(struct asn1 *ctx, uint8_t tag) -{ - ctx->p[(ctx->n)++] = tag; - return ctx->p + ctx->n; -} - -/* DER encode length and return encoded size thereof */ -static int asn1_len(uint8_t *p, size_t size) -{ - if (size < 128) { - p[0] = size; - return 1; - } else if (size < 256) { - p[0] = 0x81; - p[1] = size; - return 2; - } else { - p[0] = 0x82; - p[1] = size >> 8; - p[2] = size; - return 3; - } -} - -/* - * close sequence and move encapsulated data if needed - * return total length. - */ -static size_t asn1_seq(uint8_t *p, uint8_t tag, size_t l, size_t size) -{ - size_t tl; - - p[0] = tag; - tl = asn1_len(p + 1, size) + 1; - /* TODO: tl > l fail */ - if (tl < l) - memmove(p + tl, p + l, size); - - return tl + size; -} - -/* DER encode (small positive) integer */ -static void asn1_int(struct asn1 *ctx, uint32_t val) -{ - uint8_t *p = asn1_tag(ctx, V_ASN1_INT); - - if (!val) { - *p++ = 1; - *p++ = 0; - } else { - int nbits = 32 - __builtin_clz(val); - int nbytes = (nbits + 7) / 8; - - if ((nbits & 7) == 0) { - *p++ = nbytes + 1; - *p++ = 0; - } else { - *p++ = nbytes; - } - while (nbytes--) - *p++ = val >> (nbytes * 8); - } - - ctx->n = p - ctx->p; -} - -/* DER encode positive p256_int */ -static void asn1_p256_int(struct asn1 *ctx, const p256_int *n) -{ - uint8_t *p = asn1_tag(ctx, V_ASN1_INT); - uint8_t bn[P256_NBYTES]; - int i; - - p256_to_bin(n, bn); - for (i = 0; i < P256_NBYTES; ++i) { - if (bn[i] != 0) - break; - } - if (bn[i] & 0x80) { - *p++ = P256_NBYTES - i + 1; - *p++ = 0; - } else { - *p++ = P256_NBYTES - i; - } - for (; i < P256_NBYTES; ++i) - *p++ = bn[i]; - - ctx->n = p - ctx->p; -} - -/* DER encode p256 signature */ -static void asn1_sig(struct asn1 *ctx, const p256_int *r, const p256_int *s) -{ - SEQ_START(*ctx, V_SEQ, SEQ_SMALL) { - asn1_p256_int(ctx, r); - asn1_p256_int(ctx, s); - } - SEQ_END(*ctx); -} - -/* DER encode printable string */ -static void asn1_string(struct asn1 *ctx, uint8_t tag, const char *s) -{ - uint8_t *p = asn1_tag(ctx, tag); - size_t n = strlen(s); - - p += asn1_len(p, n); - while (n--) - *p++ = *s++; - - ctx->n = p - ctx->p; -} - -/* DER encode bytes */ -static void asn1_object(struct asn1 *ctx, size_t n, const uint8_t *b) -{ - uint8_t *p = asn1_tag(ctx, V_ASN1_OBJ); - - p += asn1_len(p, n); - while (n--) - *p++ = *b++; - - ctx->n = p - ctx->p; -} - -/* DER encode p256 pk */ -static void asn1_pub(struct asn1 *ctx, const p256_int *x, const p256_int *y) -{ - uint8_t *p = asn1_tag(ctx, 4); /* uncompressed format */ - - p256_to_bin(x, p); p += P256_NBYTES; - p256_to_bin(y, p); p += P256_NBYTES; - - ctx->n = p - ctx->p; -} - -size_t DCRYPTO_asn1_sigp(uint8_t *buf, const p256_int *r, const p256_int *s) -{ - struct asn1 asn1 = {buf, 0}; - - asn1_sig(&asn1, r, s); - return asn1.n; -} - -size_t DCRYPTO_asn1_pubp(uint8_t *buf, const p256_int *x, const p256_int *y) -{ - struct asn1 asn1 = {buf, 0}; - - asn1_pub(&asn1, x, y); - return asn1.n; -} - -/* ---- ASN.1 Parsing ---- */ - -/* - * An ASN.1 DER (Definite Encoding Rules) parser. - * Details about the format are available here: - * https://en.wikipedia.org/wiki/X.690#Definite_form - */ -static size_t asn1_parse(const uint8_t **p, size_t available, - uint8_t expected_type, const uint8_t **out, - size_t *out_len, size_t *remaining) -{ - const size_t tag_len = 1; - const uint8_t *in = *p; - size_t obj_len = 0; - size_t obj_len_bytes; - size_t consumed; - - if (available < 2) - return 0; - if (in[0] != expected_type) /* in[0] specifies the tag. */ - return 0; - - if ((in[1] & 128) == 0) { - /* Short-length encoding (i.e. obj_len <= 127). */ - obj_len = in[1]; - obj_len_bytes = 1; - } else { - int i; - - obj_len_bytes = 1 + (in[1] & 127); - if (obj_len_bytes > MAX_ASN1_OBJ_LEN_BYTES || - tag_len + obj_len_bytes > available) - return 0; - - if (in[2] == 0) - /* Definite form encoding requires minimal - * length encoding. */ - return 0; - for (i = 0; i < obj_len_bytes - 1; i++) { - obj_len <<= 8; - obj_len |= in[tag_len + 1 + i]; - } - } - - consumed = tag_len + obj_len_bytes + obj_len; - if (consumed > available) - return 0; /* Invalid object length.*/ - if (out) - *out = &in[tag_len + obj_len_bytes]; - if (out_len) - *out_len = obj_len; - - *p = in + consumed; - if (remaining) - *remaining = available - consumed; - return consumed; -} - -static size_t asn1_parse_certificate(const uint8_t **p, size_t *available) -{ - size_t consumed; - size_t obj_len; - const uint8_t *in = *p; - - consumed = asn1_parse(&in, *available, - V_ASN1_CONSTRUCTED | V_ASN1_SEQUENCE, - NULL, &obj_len, NULL); - if (consumed == 0 || consumed != *available) /* Invalid SEQUENCE. */ - return 0; - *p += consumed - obj_len; - *available -= consumed - obj_len; - return 1; -} - -static size_t asn1_parse_tbs(const uint8_t **p, size_t *available, - size_t *tbs_len) -{ - size_t consumed; - - consumed = asn1_parse(p, *available, - V_ASN1_CONSTRUCTED | V_ASN1_SEQUENCE, - NULL, NULL, available); - if (consumed == 0) - return 0; - *tbs_len = consumed; - return 1; -} - -static size_t asn1_parse_signature_algorithm(const uint8_t **p, - size_t *available) -{ - const uint8_t *alg_oid; - size_t alg_oid_len; - - if (!asn1_parse(p, *available, V_ASN1_CONSTRUCTED | V_ASN1_SEQUENCE, - &alg_oid, &alg_oid_len, available)) - return 0; - if (alg_oid_len != sizeof(OID_SHA256_WITH_RSA_ENCRYPTION)) - return 0; - if (memcmp(alg_oid, OID_SHA256_WITH_RSA_ENCRYPTION, - sizeof(OID_SHA256_WITH_RSA_ENCRYPTION)) != 0) - return 0; - return 1; -} - -static size_t asn1_parse_signature_value(const uint8_t **p, size_t *available, - const uint8_t **sig, size_t *sig_len) -{ - if (!asn1_parse(p, *available, V_ASN1_BIT_STRING, - sig, sig_len, available)) - return 0; - if (*available != 0) - return 0; /* Not all input bytes consumed. */ - return 1; -} - -/* This method verifies that the provided X509 certificate was issued - * by the specified certifcate authority. - * - * cert is a pointer to a DER encoded X509 certificate, as specified - * in https://tools.ietf.org/html/rfc5280#section-4.1. In ASN.1 - * notation, the certificate has the following structure: - * - * Certificate ::= SEQUENCE { - * tbsCertificate TBSCertificate, - * signatureAlgorithm AlgorithmIdentifier, - * signatureValue BIT STRING } - * - * TBSCertificate ::= SEQUENCE { } - * AlgorithmIdentifier ::= SEQUENCE { } - * - * where signatureValue = SIGN(HASH(tbsCertificate)), with SIGN and - * HASH specified by signatureAlgorithm. - */ -int DCRYPTO_x509_verify(const uint8_t *cert, size_t len, - const struct RSA *ca_pub_key) -{ - const uint8_t *p = cert; - const uint8_t *tbs; - size_t tbs_len; - const uint8_t *sig; - size_t sig_len; - - uint8_t digest[SHA256_DIGEST_SIZE]; - - /* Read Certificate SEQUENCE. */ - if (!asn1_parse_certificate(&p, &len)) - return 0; - - /* Read tbsCertificate SEQUENCE. */ - tbs = p; - if (!asn1_parse_tbs(&p, &len, &tbs_len)) - return 0; - - /* Read signatureAlgorithm SEQUENCE. */ - if (!asn1_parse_signature_algorithm(&p, &len)) - return 0; - - /* Read signatureValue BIT STRING. */ - if (!asn1_parse_signature_value(&p, &len, &sig, &sig_len)) - return 0; - - /* Check that the signature length corresponds to the issuer's - * public key size. */ - if (sig_len != bn_size(&ca_pub_key->N) && - sig_len != bn_size(&ca_pub_key->N) + 1) - return 0; - /* Check that leading signature bytes (if any) are zero. */ - if (sig_len == bn_size(&ca_pub_key->N) + 1) { - if (sig[0] != 0) - return 0; - sig++; - sig_len--; - } - - DCRYPTO_SHA256_hash(tbs, tbs_len, digest); - return DCRYPTO_rsa_verify(ca_pub_key, digest, sizeof(digest), - sig, sig_len, PADDING_MODE_PKCS1, HASH_SHA256); -} - -/* ---- Certificate generation ---- */ - -static void add_common_name(struct asn1 *ctx, const char *cname) -{ - SEQ_START(*ctx, V_SEQ, SEQ_SMALL) { - SEQ_START(*ctx, V_SET, SEQ_SMALL) { - SEQ_START(*ctx, V_SEQ, SEQ_SMALL) { - asn1_object(ctx, OID(commonName)); - asn1_string(ctx, V_ASN1_ASCII, cname); - } - SEQ_END(*ctx); - } - SEQ_END(*ctx); - } - SEQ_END(*ctx); -} - -int DCRYPTO_x509_gen_u2f_cert_name(const p256_int *d, const p256_int *pk_x, - const p256_int *pk_y, const p256_int *serial, - const char *name, uint8_t *cert, const int n) -{ - struct asn1 ctx = {cert, 0}; - HASH_CTX sha; - p256_int h, r, s; - struct drbg_ctx drbg; - - SEQ_START(ctx, V_SEQ, SEQ_LARGE) { /* outer seq */ - /* - * Grab current pointer to data to hash later. - * Note this will fail if cert body + cert sign is less - * than 256 bytes (SEQ_MEDIUM) -- not likely. - */ - uint8_t *body = ctx.p + ctx.n; - - /* Cert body seq */ - SEQ_START(ctx, V_SEQ, SEQ_MEDIUM) { - /* X509 v3 */ - SEQ_START(ctx, 0xa0, SEQ_SMALL) { - asn1_int(&ctx, 2); - } - SEQ_END(ctx); - - /* Serial number */ - if (serial) - asn1_p256_int(&ctx, serial); - else - asn1_int(&ctx, 1); - - /* Signature algo */ - SEQ_START(ctx, V_SEQ, SEQ_SMALL) { - asn1_object(&ctx, OID(ecdsa_with_SHA256)); - } - SEQ_END(ctx); - - /* Issuer */ - add_common_name(&ctx, name); - - /* Expiry */ - SEQ_START(ctx, V_SEQ, SEQ_SMALL) { - asn1_string(&ctx, V_ASN1_TIME, "20000101000000Z"); - asn1_string(&ctx, V_ASN1_TIME, "20991231235959Z"); - } - SEQ_END(ctx); - - /* Subject */ - add_common_name(&ctx, name); - - /* Subject pk */ - SEQ_START(ctx, V_SEQ, SEQ_SMALL) { - /* pk parameters */ - SEQ_START(ctx, V_SEQ, SEQ_SMALL) { - asn1_object(&ctx, OID(id_ecPublicKey)); - asn1_object(&ctx, OID(prime256v1)); - } - SEQ_END(ctx); - /* pk bits */ - SEQ_START(ctx, V_BITS, SEQ_SMALL) { - /* No unused bit at the end */ - asn1_tag(&ctx, 0); - asn1_pub(&ctx, pk_x, pk_y); - } - SEQ_END(ctx); - } - SEQ_END(ctx); - - /* U2F transports indicator extension */ - SEQ_START(ctx, 0xa3, SEQ_SMALL) { - SEQ_START(ctx, V_SEQ, SEQ_SMALL) { - SEQ_START(ctx, V_SEQ, SEQ_SMALL) { - asn1_object(&ctx, OID(fido_u2f)); - SEQ_START(ctx, V_ASN1_BYTES, SEQ_SMALL) { - SEQ_START(ctx, V_BITS, SEQ_SMALL) { - /* 3 zero bits */ - asn1_tag(&ctx, 3); - /* usb-internal transport */ - asn1_tag(&ctx, 0x08); - } - SEQ_END(ctx); - } - SEQ_END(ctx); - } - SEQ_END(ctx); - } - SEQ_END(ctx); - } - SEQ_END(ctx); - } - SEQ_END(ctx); /* Cert body */ - - /* Sign all of cert body */ - DCRYPTO_SHA256_init(&sha, 0); - HASH_update(&sha, body, (ctx.p + ctx.n) - body); - p256_from_bin(HASH_final(&sha), &h); - hmac_drbg_init_rfc6979(&drbg, d, &h); - if (!dcrypto_p256_ecdsa_sign(&drbg, d, &h, &r, &s)) - return 0; - - /* Append X509 signature */ - SEQ_START(ctx, V_SEQ, SEQ_SMALL); - asn1_object(&ctx, OID(ecdsa_with_SHA256)); - SEQ_END(ctx); - SEQ_START(ctx, V_BITS, SEQ_SMALL) { - /* no unused/zero bit at the end */ - asn1_tag(&ctx, 0); - asn1_sig(&ctx, &r, &s); - } SEQ_END(ctx); - - } SEQ_END(ctx); /* end of outer seq */ - - return ctx.n; -} - -int DCRYPTO_x509_gen_u2f_cert(const p256_int *d, const p256_int *pk_x, - const p256_int *pk_y, const p256_int *serial, - uint8_t *cert, const int n) -{ - return DCRYPTO_x509_gen_u2f_cert_name(d, pk_x, pk_y, serial, - serial ? STRINGIFY(BOARD) : "U2F", - cert, n); -} |