6db4831e98
Android 14
217 lines
5.2 KiB
C
217 lines
5.2 KiB
C
/*
|
|
* 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;
|
|
}
|