/* * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This code is originated from Samsung Electronics proprietary sources. * Author: Viacheslav Vovchenko, * Created: 10 Jul 2017 * * Copyright (C) 2016 Samsung Electronics, Inc. */ #include #include #include "five_crypto.h" #include "five_cert.h" int five_cert_body_alloc(struct five_cert_header *header, uint8_t *hash, size_t hash_len, uint8_t *label, size_t label_len, uint8_t **raw_cert, size_t *raw_cert_len) { size_t data_len; uint8_t *data; struct lv *next; const void *end; int rc = 0; struct five_cert_body body_cert = {0}; if (unlikely(!header || !raw_cert || !raw_cert_len)) return -EINVAL; data_len = sizeof(*body_cert.header) + sizeof(*header) + sizeof(*body_cert.hash) + hash_len + sizeof(*body_cert.label) + label_len; if (unlikely(data_len > FIVE_MAX_CERTIFICATE_SIZE)) return -EINVAL; data = kzalloc(data_len, GFP_NOFS); if (unlikely(!data)) return -ENOMEM; next = body_cert.header = (struct lv *)data; end = data + data_len; /* Fill header data */ rc = lv_set(next, header, sizeof(*header), end); if (unlikely(rc)) goto exit; next = body_cert.hash = lv_get_next(next, end); if (unlikely(!next)) goto exit; /* Fill hash data */ rc = lv_set(next, hash, hash_len, end); if (unlikely(rc)) goto exit; next = body_cert.label = lv_get_next(next, end); if (unlikely(!next)) goto exit; /* Fill label data */ rc = lv_set(next, label, label_len, end); if (unlikely(rc)) goto exit; exit: if (likely(!rc)) { *raw_cert = data; *raw_cert_len = data_len; } else { kfree(data); } return rc; } void five_cert_free(void *raw_cert) { kfree(raw_cert); } int five_cert_append_signature(void **raw_cert, size_t *raw_cert_len, void *signature, size_t signature_len) { int rc = 0; void *new_raw_cert; size_t new_raw_cert_len; const void *end; struct five_cert cert = { {0} }; if (unlikely(!raw_cert || !raw_cert_len || !signature)) return -EINVAL; new_raw_cert_len = *raw_cert_len + sizeof(cert.signature->length) + signature_len; if (unlikely(new_raw_cert_len > FIVE_MAX_CERTIFICATE_SIZE)) return -EINVAL; new_raw_cert = krealloc(*raw_cert, new_raw_cert_len, GFP_NOFS); if (unlikely(!new_raw_cert)) return -ENOMEM; *raw_cert = new_raw_cert; *raw_cert_len = new_raw_cert_len; end = (uint8_t *)new_raw_cert + new_raw_cert_len; rc = five_cert_fillout(&cert, new_raw_cert, new_raw_cert_len); if (unlikely(rc)) return rc; rc = lv_set(cert.signature, signature, signature_len, end); return rc; } int five_cert_body_fillout(struct five_cert_body *body_cert, const void *raw_cert, size_t raw_cert_len) { struct lv *next; const void *end; struct five_cert_header *hdr; if (unlikely(!body_cert || !raw_cert || raw_cert_len > FIVE_MAX_CERTIFICATE_SIZE)) return -EINVAL; next = body_cert->header = (struct lv *)raw_cert; end = (uint8_t *)raw_cert + raw_cert_len; /* Check if we had an error at the previous step */ if (!lv_get_next(next, end)) return -EINVAL; if (sizeof(*hdr) != body_cert->header->length) return -EINVAL; hdr = (struct five_cert_header *)body_cert->header->value; if (hdr->version != FIVE_CERT_VERSION1) return -EINVAL; next = body_cert->hash = lv_get_next(next, end); if (unlikely(!next)) return -EINVAL; next = body_cert->label = lv_get_next(next, end); if (unlikely(!next)) return -EINVAL; return 0; } int five_cert_fillout(struct five_cert *cert, const void *raw_cert, size_t raw_cert_len) { int rc; struct lv *next; const void *end; if (unlikely(!cert || !raw_cert)) return -EINVAL; if (unlikely(raw_cert_len > FIVE_MAX_CERTIFICATE_SIZE)) return -EINVAL; rc = five_cert_body_fillout(&cert->body, raw_cert, raw_cert_len); if (unlikely(rc)) return rc; next = cert->body.label; end = (uint8_t *)raw_cert + raw_cert_len; if (!lv_get_next(next, end)) return -EINVAL; next = cert->signature = lv_get_next(next, end); if (unlikely(!next)) return -EINVAL; return 0; } int five_cert_calc_hash(struct five_cert_body *body_cert, uint8_t *out_hash, size_t *out_hash_len) { int rc; size_t hash_len, body_len; struct five_cert_header *header; if (unlikely(!body_cert || !out_hash || !out_hash_len)) return -EINVAL; hash_len = *out_hash_len; header = (struct five_cert_header *)body_cert->header->value; body_len = sizeof(*body_cert->header) + body_cert->header->length + sizeof(*body_cert->hash) + body_cert->hash->length + sizeof(*body_cert->label) + body_cert->label->length; if (unlikely(body_len > FIVE_MAX_CERTIFICATE_SIZE)) return -EINVAL; rc = five_calc_data_hash((const uint8_t *)body_cert->header, body_len, header->hash_algo, out_hash, &hash_len); if (unlikely(rc)) return rc; *out_hash_len = hash_len; return 0; }