Index: lib/libsecureboot/Makefile =================================================================== --- /dev/null +++ lib/libsecureboot/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ +PACKAGE=lib${LIB} + +.include + +LIB= secureboot + +.include "${SRCTOP}/lib/libbearssl/Makefile.inc" + +INCS= secureboot.h +SRCS= gen_sha256.c get_tbs.c util.c verify_cert.c + +.include Index: lib/libsecureboot/Makefile.inc =================================================================== --- /dev/null +++ lib/libsecureboot/Makefile.inc @@ -0,0 +1,7 @@ +# $FreeBSD$ + +SECUREBOOT_SRC= ${SRCTOP}/lib/libsecureboot + +.PATH: ${SECUREBOOT_SRC} + +CFLAGS+= -I${SECUREBOOT_SRC} Index: lib/libsecureboot/Makefile.libsa.inc =================================================================== --- /dev/null +++ lib/libsecureboot/Makefile.libsa.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +.include "Makefile.inc" + +SRCS+= gen_sha256.c get_tbs.c util.c verify_cert.c Index: lib/libsecureboot/gen_sha256.c =================================================================== --- /dev/null +++ lib/libsecureboot/gen_sha256.c @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#define CHUNK_SIZE 4096 + +/* + * Hash length bytes from fd using SHA256 + * and store the result in output. + */ +int +gen_digest(int fd, size_t length, unsigned char *output) +{ + br_hash_compat_context hash_ctx; + const br_hash_class *md; + char chunk[CHUNK_SIZE]; + int bytes; + + md = &br_sha256_vtable; + + if (fd < 0 || length == 0 || output == NULL) + return (EINVAL); + + md->init(&hash_ctx.vtable); + + while (length > 0) { + if (length > CHUNK_SIZE) + bytes = CHUNK_SIZE; + else + bytes = length; + + bytes = checked_read(fd, chunk, bytes); + if (bytes <= 0) { + return (-bytes); + } + md->update(&hash_ctx.vtable, chunk, bytes); + length -= bytes; + } + + md->out(&hash_ctx.vtable, output); + + return (0); +} Index: lib/libsecureboot/get_tbs.c =================================================================== --- /dev/null +++ lib/libsecureboot/get_tbs.c @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +/* ASN parsing related defines */ +#define ASN1_CONSTRUCTED BIT(5) +#define ASN1_PRIMITIVE_TAG 0x1F +#define ASN1_INF_LENGTH 0x80 +#define ASN1_LENGTH_MASK 0x7F + +/* + * Get TBS part of certificate. + * Since BearSSL doesn't provide any API to do this, + * it has to be implemented here. + */ +void* +X509_to_tbs(unsigned char* cert, size_t* output_size) +{ + unsigned char *result; + size_t tbs_size; + int size; + int i; + + if (cert == NULL) + return NULL; + + /* Strip two sequences to get to the TBS section */ + for (i = 0; i < 2; i++) { + /* + * XXX: We don't need to support extended tags since + * they should not be present in certificates. + */ + if ((*cert & ASN1_PRIMITIVE_TAG) == ASN1_PRIMITIVE_TAG) + return (NULL); + + cert++; + + if (*cert == ASN1_INF_LENGTH) + return (NULL); + + size = *cert & ASN1_LENGTH_MASK; + tbs_size = 0; + + /* Size can either be stored on a single or multiple bytes */ + if (*cert & (ASN1_LENGTH_MASK + 1)) { + cert++; + while (*cert == 0 && size > 0) { + cert++; + size--; + } + while(size-- > 0) { + tbs_size <<= 8; + tbs_size |= *(cert++); + } + } + if (i == 0) + result = cert; + } + tbs_size += (cert - result); + + if (output_size != NULL) + *output_size = tbs_size; + + return (result); +} Index: lib/libsecureboot/secureboot.h =================================================================== --- /dev/null +++ lib/libsecureboot/secureboot.h @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SECUREBOOT_H +#define SECUREBOOT_H + +#include +__FBSDID("$FreeBSD$"); + +#ifdef _STANDALONE +#include +#else +#include +#include +#include +#include +#include +#endif /* _STANDALONE */ + +#include + +#define MAGIC 0x05ECB00705ECB007 + +#pragma pack(1) + +struct signature_info { + uint64_t magic; + uint64_t kernel_size; + uint64_t signature_size; + uint64_t cert_size; +}; + +#pragma pack() + +struct vector { + char *data; + size_t capacity; + size_t length; +}; + +int gen_digest(int, size_t, unsigned char*); +void* X509_to_tbs(unsigned char*, size_t*); +br_x509_pkey * verify_cert(br_x509_certificate*, br_x509_certificate*, size_t); + +/* Small helper functions */ +ssize_t checked_read(int, char*, size_t); +void vector_append(void*, const void*, size_t); +int copy_pkey(br_x509_pkey*, const br_x509_pkey*); +void free_anchors(br_x509_trust_anchor*, size_t); +void free_certs(br_x509_certificate*, size_t); +void free_pkey(br_x509_pkey*); + +#endif /* SECUREBOOT_H */ + Index: lib/libsecureboot/util.c =================================================================== --- /dev/null +++ lib/libsecureboot/util.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +/* + * Read leangth bytes from file fd. + * If we can't read everything in a single read, + * repeat until we get all we need, + * or hit EOF. + */ +ssize_t +checked_read(int fd, char *buf, size_t length) +{ + ssize_t result; + size_t bytes_read = 0; + + while(length != 0) { + result = read(fd, buf, length); + + if (result < 0) { + printf("Read error %d\n", errno); + return (-errno); + } + if (result == 0) + break; + + length -= result; + bytes_read += result; + buf += result; + } + return (bytes_read); +} + +/* + * Helper function used as callback in BearSSL. + * It simply appends data to existing vector structure. + */ +void +vector_append(void *ctx, const void *src, size_t len) +{ + struct vector *vec = (struct vector*) ctx; + + if (vec->data == NULL) + return; + + if (vec->capacity - vec->length < len) { + vec->data = realloc(vec->data, 2*vec->capacity); + if (vec->data == NULL) + return; + + vec->capacity *= 2; + } + + memcpy((vec->data + vec->length), src, len); + vec->length += len; +} + +/* + * Helper function that allocates memory and duplicates a public key. + */ +int +copy_pkey(br_x509_pkey *dest, const br_x509_pkey *src) +{ + if (src->key_type != BR_KEYTYPE_RSA) + return (EINVAL); + + dest->key_type = src->key_type; + dest->key.rsa.nlen = src->key.rsa.nlen; + dest->key.rsa.elen = src->key.rsa.elen; + + dest->key.rsa.n = malloc(dest->key.rsa.nlen); + if (dest->key.rsa.n == NULL) + return (ENOMEM); + + dest->key.rsa.e = malloc(dest->key.rsa.elen); + if (dest->key.rsa.e == NULL) + return (ENOMEM); + + memcpy(dest->key.rsa.n, src->key.rsa.n, dest->key.rsa.nlen); + memcpy(dest->key.rsa.e, src->key.rsa.e, dest->key.rsa.elen); + + return (0); +} + +/* + * Routines used for memory management. + */ +void +free_certs(br_x509_certificate *certs, size_t count) +{ + if (certs == NULL) + return; + + while(count-- > 0) + free(certs[count].data); + + free(certs); +} + +void +free_anchors(br_x509_trust_anchor *anchors, size_t count) +{ + if (anchors == NULL) + return; + + while(count-- > 0) { + free(anchors[count].dn.data); + free_pkey(&anchors[count].pkey); + } + + free(anchors); +} + +void +free_pkey(br_x509_pkey *pkey) +{ + free(pkey->key.rsa.n); + free(pkey->key.rsa.e); +} Index: lib/libsecureboot/verify_cert.c =================================================================== --- /dev/null +++ lib/libsecureboot/verify_cert.c @@ -0,0 +1,164 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#define DEFAULT_DN_SIZE 128 + +/* + * Convert X509 certificate to a br_x509_trust_anchor format + * that can be used for verification in the "minimal" engine. + */ +static int +get_anchor(const br_x509_certificate *cert, br_x509_trust_anchor *result) +{ + br_x509_decoder_context decoder_ctx; + br_x509_trust_anchor anchor; + br_x509_pkey *pkey; + struct vector vec; + int error; + + anchor.dn.data = NULL; + anchor.pkey.key.rsa.n = NULL; + anchor.pkey.key.rsa.e = NULL; + + /* + * Allocate memory for dn, that will be populated in callback. + */ + vec.data = malloc(DEFAULT_DN_SIZE); + if (vec.data == NULL) { + printf("Failed to allocate memory\n"); + error = ENOMEM; + goto fail; + } + vec.capacity = DEFAULT_DN_SIZE; + vec.length = 0; + + br_x509_decoder_init(&decoder_ctx, vector_append, &vec); + br_x509_decoder_push(&decoder_ctx, cert->data, cert->data_len); + pkey = br_x509_decoder_get_pkey(&decoder_ctx); + + error = copy_pkey(&anchor.pkey, pkey); + if (error != 0) + goto fail; + + anchor.dn.data = vec.data; + anchor.dn.len = vec.length; + + if (anchor.dn.data == NULL) + goto fail; + + /* + * Check if parsed certificate has the CA bit. + * If that is not the case the verification engine + * will only look for a direct match against + * the verified certificate. + */ + if (br_x509_decoder_isCA(&decoder_ctx)) + anchor.flags = BR_X509_TA_CA; + else + anchor.flags = 0; + + memcpy(result, &anchor, sizeof(br_x509_trust_anchor)); + return (0); + +fail: + if (anchor.dn.data != NULL) + free(anchor.dn.data); + + if (anchor.pkey.key.rsa.n != NULL) + free(anchor.pkey.key.rsa.n); + + if (anchor.pkey.key.rsa.e != NULL) + free(anchor.pkey.key.rsa.e); + + return (error); +} + +/* + * Verify the cert using the BearSSL "minimal" engine. + * Return public key of verified certificate on success and NULL otherwise. + */ +br_x509_pkey * +verify_cert(br_x509_certificate *cert, br_x509_certificate *trusted_certs, size_t num) +{ + br_x509_minimal_context ctx; + br_x509_trust_anchor *anchors; + const br_x509_pkey *result_pkey; + br_x509_pkey *pkey; + unsigned usages; + int error; + unsigned int i; + + if (cert == NULL || trusted_certs == NULL || num == 0) + return (NULL); + + anchors = malloc(sizeof(br_x509_trust_anchor) * num); + if (anchors == NULL) + return (NULL); + + for (i = 0; i < num; i++) { + error = get_anchor(&trusted_certs[i], &anchors[i]); + if (error != 0) + return (NULL); + } + + br_x509_minimal_init(&ctx, &br_sha256_vtable, anchors, num); + br_x509_minimal_set_hash(&ctx, br_sha256_ID, &br_sha256_vtable); + br_x509_minimal_set_rsa(&ctx, br_rsa_pkcs1_vrfy_get_default()); + + /* + * We currently don't support chain certificates. + */ + ctx.vtable->start_chain(&ctx.vtable, NULL); + ctx.vtable->start_cert(&ctx.vtable, cert->data_len); + ctx.vtable->append(&ctx.vtable, cert->data, cert->data_len); + ctx.vtable->end_cert(&ctx.vtable); + error = ctx.vtable->end_chain(&ctx.vtable); + + result_pkey = ctx.vtable->get_pkey(&ctx.vtable, &usages); + free_anchors(anchors, num); + + if (error != 0) + return (NULL); + + if (ctx.err != BR_ERR_X509_OK && ctx.err != 0) + return (NULL); + + pkey = malloc(sizeof(br_x509_pkey)); + if (pkey == NULL || copy_pkey(pkey, result_pkey) != 0) { + if (pkey != NULL) + free(pkey); + + return (NULL); + } + + return (pkey); +}