Project-Cerberus/core/asn1/asn1_util.c

339 строки
8.3 KiB
C

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdint.h>
#include <string.h>
#include "asn1_util.h"
/**
* Type tags used in ASN.1 encoding.
*/
enum {
ASN1_TAG_INTEGER = 0x02, /**< ASN.1 INTEGER value. */
ASN1_TAG_OBJECT_IDENTIFIER = 0x06, /**< ASN.1 OBJECT IDENTIFIER value. */
};
/**
* Parse a DER encoded ASN.1 header to determine the encoded item length.
*
* @param der The DER formatted data pointing to the beginning of the header to parse.
* @param der_length Total length of the DER data.
* @param item_length Output for the length of the encoded item.
* @param header_length Output for the length ASN.1 header on the item.
*
* @return 0 if the header was parse successfully or ASN1_UTIL_NOT_VALID if the length cannot be
* determined.
*/
static int asn1_parse_der_header (const uint8_t *der, size_t der_length, size_t *item_length,
size_t *header_length)
{
if (der_length < 2) {
return ASN1_UTIL_NOT_VALID;
}
switch (der[1]) {
case 0x81:
if (der_length < 3) {
return ASN1_UTIL_NOT_VALID;
}
*header_length = 3;
*item_length = der[2];
break;
case 0x82:
if (der_length < 4) {
return ASN1_UTIL_NOT_VALID;
}
*header_length = 4;
*item_length = (der[2] << 8) | der[3];
break;
case 0x83:
if (der_length < 5) {
return ASN1_UTIL_NOT_VALID;
}
*header_length = 5;
*item_length = (der[2] << 16) | (der[3] << 8) | der[4];
break;
case 0x84:
if (der_length < 6) {
return ASN1_UTIL_NOT_VALID;
}
*header_length = 6;
*item_length = (der[2] << 24) | (der[3] << 16) | (der[4] << 8) | der[5];
break;
default:
if (der[1] < 0x80) {
*header_length = 2;
*item_length = der[1];
}
else {
return ASN1_UTIL_NOT_VALID;
}
break;
}
return 0;
}
/**
* Get the next item in a DER encoded ASN.1 buffer.
*
* @param tag The expected tag for the next item in the buffer.
* @param der Buffer that contains the DER encoded data. This must point to the beginning of the
* header for the next item. Upon output, this pointer will be updated to point to the item data.
* @param der_length Total length of the DER buffer. Upon output, this will be updated with the
* remaining length of DER data.
* @param length Output for the length of the encoded item.
*
* @return 0 if the item was retrieved successfully or an error code.
*/
static int asn1_get_item (uint8_t tag, const uint8_t **der, size_t *der_length, size_t *length)
{
size_t header_length;
int status;
status = asn1_parse_der_header (*der, *der_length, length, &header_length);
if (status != 0) {
return status;
}
if ((*der)[0] != tag) {
return ASN1_UTIL_UNEXPECTED_TAG;
}
if ((size_t) (header_length + *length) > *der_length) {
return ASN1_UTIL_SMALL_DER_BUFFER;
}
*der += header_length;
*der_length -= header_length;
return 0;
}
/**
* Get the total length of a ASN.1 item from DER formatted data.
*
* @param der The DER formatted ASN.1 data.
* @param der_len Total length of DER buffer.
*
* @return The total length of the item, including the header, or ASN1_UTIL_NOT_VALID if the length
* cannot be determined. A null buffer will result in a length of 0.
*/
int asn1_get_der_item_len (const uint8_t *der, size_t der_len)
{
size_t header_len;
size_t length;
int status;
if (der == NULL) {
return 0;
}
status = asn1_parse_der_header (der, der_len, &length, &header_len);
if (status == 0) {
status = length + header_len;
}
return status;
}
/**
* Inspect DER encoded ASN.1 data to determine the total length of the data. The length
* will be returned with the following conditions:
* - If the encoded length is less than buffer length, the encoded length will be returned.
* - If the buffer length is less than or equal to the encoded length, the buffer length will be
* returned.
* - If the encoded length cannot be determined, the buffer length will be returned.
* - If the DER buffer is null, 0 will be returned.
*
* @param der ASN.1/DER encoded data to inspect.
* @param max_length Length of the data buffer containing the ASN.1/DER data.
*
* @return Length of the encoded data contained within the buffer.
*/
size_t asn1_get_der_encoded_length (const uint8_t *der, size_t max_length)
{
size_t der_length;
der_length = asn1_get_der_item_len (der, max_length);
if (max_length <= der_length) {
return max_length;
}
else {
return der_length;
}
}
/**
* Encode an integer value as a DER encoded ASN.1 INTEGER.
*
* @param value The value to encode.
* @param der Output for the DER encoded data.
* @param length Length of the DER output buffer.
*
* @return Length of the encoded data or an error code.
*/
int asn1_encode_integer (uint64_t value, uint8_t *der, size_t length)
{
size_t i = 56;
size_t der_length = 0;
if (der == NULL) {
return ASN1_UTIL_INVALID_ARGUMENT;
}
if (length < 3) {
/* Every encoded integer will be at least 3 bytes. */
return ASN1_UTIL_SMALL_DER_BUFFER;
}
/* Find the first non-zero byte. */
while (((i > 0) && ((value >> i) & 0xff) == 0)) {
i -= 8;
}
/* Add an extra zero if the sign bit would be negative. */
if ((value >> i) & 0x80) {
der[2] = 0;
der_length++;
}
/* Add all bytes after the first non-zero byte, but make sure the LSB is always added. */
i += 8;
do {
i -= 8;
if (length > (2 + der_length)) {
der[2 + der_length++] = value >> i;
}
else {
return ASN1_UTIL_SMALL_DER_BUFFER;
}
} while (i > 0);
/* INTEGER tag and length. */
der[0] = ASN1_TAG_INTEGER;
der[1] = der_length;
return 2 + der_length;
}
/**
* Decode an integer value that is ASN.1/DER encoded.
*
* @param der The DER encoded integer value.
* @param length Length of the DER buffer.
* @param value Output for the decoded integer value.
*
* @return 0 if the value was decoded successfully or an error code.
*/
int asn1_decode_integer (const uint8_t *der, size_t length, uint64_t *value)
{
size_t i;
size_t der_length;
uint64_t tmp = 0;
int status;
if ((der == NULL) || (value == NULL)) {
return ASN1_UTIL_INVALID_ARGUMENT;
}
status = asn1_get_item (ASN1_TAG_INTEGER, &der, &length, &der_length);
if (status != 0) {
return status;
}
if (der[0] & 0x80) {
/* This API does not support negative integers. */
return ASN1_UTIL_OUT_OF_RANGE;
}
if ((der_length > 9) || ((der_length == 9) && (der[0] != 0))) {
/* Anything larger than 8 bytes will not fit into a 64-bit integer. */
return ASN1_UTIL_OUT_OF_RANGE;
}
for (i = 0; i < der_length; i++) {
tmp = (tmp << 8) + der[i];
}
*value = tmp;
return 0;
}
/**
* Encode an OID value as a DER encoded ASN.1 OBJECT IDENTIFIER. The OID value itself must already
* be base128 encoded per DER rules. No additional processing of the value will be performed.
*
* @param oid The base128 encoded OID to DER encode.
* @param oid_length Length of the OID data.
* @param der Output for the DER encoded data.
* @param length Length of the DER output buffer.
*
* @return Length of the encoded data or an error code.
*/
int asn1_encode_base128_oid (const uint8_t *oid, size_t oid_length, uint8_t *der, size_t length)
{
size_t der_length = 2 + oid_length;
if ((oid == NULL) || (oid_length == 0) || (der == NULL)) {
return ASN1_UTIL_INVALID_ARGUMENT;
}
if (oid_length > 127) {
/* Most OIDs are short, so assume there will never be an OID that requires more than one
* length byte for encoding. */
return ASN1_UTIL_OUT_OF_RANGE;
}
if (length < der_length) {
return ASN1_UTIL_SMALL_DER_BUFFER;
}
der[0] = 0x06;
der[1] = oid_length;
memcpy (&der[2], oid, oid_length);
return der_length;
}
/**
* Decode an OID value that is ASN.1/DER encoded. The OID will remain base128 encoded per DER
* rules.
*
* @param der The DER encoded object identifier.
* @param length Length of the DER buffer.
* @param oid Output for the decoded OID. This will be a pointer to the value in the DER buffer, so
* is only valid as long as the DER buffer is valid.
* @param oid_length Output for the length of the OID data.
*
* @return 0 if the OID was decoded successfully or an error code.
*/
int asn1_decode_base128_oid (const uint8_t *der, size_t length, const uint8_t **oid,
size_t *oid_length)
{
if ((der == NULL) || (oid == NULL) || (oid_length == NULL)) {
return ASN1_UTIL_INVALID_ARGUMENT;
}
*oid = der;
return asn1_get_item (ASN1_TAG_OBJECT_IDENTIFIER, oid, &length, oid_length);
}