kernel_samsung_a34x-permissive/security/samsung/five/five_cert.c

217 lines
5.2 KiB
C
Raw Normal View History

/*
* 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, <v.vovchenko@samsung.com>
* Created: 10 Jul 2017
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*/
#include <linux/module.h>
#include <linux/slab.h>
#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;
}