core/riot/reference/RiotDerDec.c (323 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include "platform_api.h" #include "asn1/x509_cert_build.h" #include "include/RiotDerDec.h" #include "include/RiotStatus.h" #include "include/RiotX509Bldr.h" #define ASRT(_X) if(!(_X)) {goto Error;} #define CHK(_X) if(((_X)) < 0) {goto Error;} // // This file contains basic DER-decoding routines that are sufficient to create // RIoT X.509 certificates. // // Routines in this file encode the following types: // SEQUENCE // SET // INTEGER // BIT STRING // OCTET STRING // OID // UTF8String // // This file also contains helper DER-decoding routines to create RIoT X.509 certificates. // static int read_length (size_t *len, const uint8_t *der, size_t der_len, size_t *position) { size_t length; uint8_t header; size_t pos = *position; if (pos >= der_len) { return -1; } switch (der[pos]) { case 0x81: header = 2; if ((pos + 1) >= der_len) { return -1; } length = (size_t) der[pos + 1]; break; case 0x82: header = 3; if ((pos + 2) >= der_len) { return -1; } length = (size_t) ((der[pos + 1] << 8) | der[pos + 2]); break; case 0x83: header = 4; if ((pos + 3) >= der_len) { return -1; } length = (size_t) ((der[pos + 1] << 16) | (der[pos + 2] << 8) | der[pos + 3]); break; case 0x84: header = 5; if ((pos + 4) >= der_len) { return -1; } length = (size_t) ((der[pos + 1] << 24) | (der[pos + 2] << 16) | (der[pos + 3] << 8) | der[pos + 4]); break; default: if (der[pos] < 0x80) { header = 1; length = (size_t) der[pos]; break; } return -1; } if (len != NULL) { *len = length; } (*position) += (uint32_t) header; return 0; } static int process_asn1_type (size_t *len, size_t *position, const uint8_t *der_buf, size_t der_len, uint8_t tag) { if (*position >= der_len) { return -1; } if (der_buf[*position] != tag) { return -1; } (*position) += 1; return read_length (len, der_buf, der_len, position); } static int process_asn1_explicit_type (size_t *len, size_t *position, const uint8_t *der_buf, size_t der_len, uint8_t tag_num) { if (*position >= der_len) { return -1; } if (der_buf[*position] != (0xA0 + tag_num)) { return -1; } (*position) += 1; return read_length (len, der_buf, der_len, position); } static int read_set (const uint8_t *der_buf, size_t der_len, size_t *position) { return process_asn1_type (NULL, position, der_buf, der_len, 0x31); } static int read_integer (const uint8_t *der_buf, size_t der_len, size_t *position) { size_t len; int status; status = process_asn1_type (&len, position, der_buf, der_len, 0x02); if (status != 0) { return -1; } (*position) += len; return 0; } static int read_explicit_integer (const uint8_t *der_buf, size_t der_len, size_t *position, uint8_t tag_num) { size_t len; int status; status = process_asn1_explicit_type (&len, position, der_buf, der_len, tag_num); if (status != 0) { return -1; } return read_integer (der_buf, der_len, position); } static int read_explicit_type (const uint8_t *der_buf, size_t der_len, size_t *position, uint8_t tag_num) { size_t len; int status; status = process_asn1_explicit_type (&len, position, der_buf, der_len, tag_num); if (status != 0) { return -1; } (*position) += len; return 0; } static int decode_bit_string (const uint8_t **bit_str, size_t *out_len, const uint8_t *der_buf, size_t der_len, size_t *position) { size_t bstr_len; int status; status = process_asn1_type (&bstr_len, position, der_buf, der_len, 0x03); if ((status != 0) || ((*position + bstr_len) > der_len)) { return -1; } *bit_str = &der_buf[*position]; if (out_len) { *out_len = bstr_len; } return 0; } static int decode_explicit_bit_string (const uint8_t **bit_str, size_t *out_len, const uint8_t *der_buf, size_t der_len, size_t *position, uint8_t tag_num) { size_t len; int status; status = process_asn1_explicit_type (&len, position, der_buf, der_len, tag_num); if (status != 0) { return -1; } status = decode_bit_string (bit_str, out_len, der_buf, der_len, position); if (status != 0) { return -1; } return 0; } static int decode_octet_string (uint8_t *oct_str, size_t *out_len, size_t max_buf_len, const uint8_t *der_buf, size_t der_len, size_t *position) { size_t len; int status; status = process_asn1_type (&len, position, der_buf, der_len, 0x04); if ((status != 0) || ((*position + len) > der_len)) { return -1; } if (len > max_buf_len) { return -2; } memcpy (oct_str, &der_buf[*position], len); (*position) += len; if (out_len) { *out_len = len; } return 0; } static int read_octet_string (size_t *len, const uint8_t *der_buf, size_t der_len, size_t *position) { return process_asn1_type (len, position, der_buf, der_len, 0x04); } static int read_oid (size_t *len, const uint8_t *der_buf, size_t der_len, size_t *position) { return process_asn1_type (len, position, der_buf, der_len, 0x06); } static int read_utf8String (size_t *len, const uint8_t *der_buf, size_t der_len, size_t *position) { return process_asn1_type (len, position, der_buf, der_len, 0xc); } static int read_sequence (size_t *len, const uint8_t *der_buf, size_t der_len, size_t *position) { return process_asn1_type (len, position, der_buf, der_len, 0x30); } static int read_cert_subject_name (size_t *len, size_t *position, const uint8_t *der, size_t der_len) { CHK (read_sequence (NULL, der, der_len, position)); CHK (read_sequence (NULL, der, der_len, position)); CHK (read_explicit_integer (der, der_len, position, 0)); CHK (read_integer (der, der_len, position)); CHK (read_sequence (len, der, der_len, position)); (*position) += (*len); CHK (read_sequence (NULL, der, der_len, position)); CHK (read_set (der, der_len, position)); CHK (read_sequence (NULL, der, der_len, position)); CHK (read_oid (len, der, der_len, position)); (*position) += (*len); CHK (read_utf8String (len, der, der_len, position)); (*position) += (*len); CHK (read_sequence (len, der, der_len, position)); (*position) += (*len); CHK (read_sequence (NULL, der, der_len, position)); CHK (read_set (der, der_len, position)); CHK (read_sequence (NULL, der, der_len, position)); CHK (read_oid (len, der, der_len, position)); (*position) += (*len); CHK (read_utf8String (len, der, der_len, position)); return 0; Error: return -1; } RIOT_STATUS DERDECReadSequence (size_t *len, const uint8_t *der_buf, size_t der_len, size_t *position) { CHK (read_sequence (len, der_buf, der_len, position)); return RIOT_SUCCESS; Error: return RIOT_FAILURE; } RIOT_STATUS DERDECGetPrivKey (uint8_t *private_key, size_t *key_len, const uint8_t *private_key_der, const size_t key_der_len) { size_t position = 0; int status; if ((private_key == NULL) || (private_key_der == NULL)) { goto Error; } ASRT (key_der_len <= RIOT_X509_MAX_KEY_LEN); CHK (read_sequence (NULL, private_key_der, key_der_len, &position)); CHK (read_integer (private_key_der, key_der_len, &position)); status = decode_octet_string (private_key, key_len, RIOT_ECC_PRIVATE_BYTES, private_key_der, key_der_len, &position); if (status == -2) { return RIOT_INVALID_PARAMETER; } else if (status < 0) { goto Error; } if (key_len) { ASRT (*key_len <= RIOT_ECC_PRIVATE_BYTES); } return RIOT_SUCCESS; Error: return RIOT_FAILURE; } RIOT_STATUS DERDECGetPubKey (const uint8_t **public_key, size_t *key_len, const uint8_t *public_key_der, const size_t key_der_len) { size_t position = 0; size_t len; if ((public_key == NULL) || (public_key_der == NULL)) { goto Error; } CHK (read_sequence (NULL, public_key_der, key_der_len, &position)); CHK (read_sequence (&len, public_key_der, key_der_len, &position)); position += len; CHK (decode_bit_string (public_key, key_len, public_key_der, key_der_len, &position)); (*public_key)++; (*key_len)--; return RIOT_SUCCESS; Error: return RIOT_FAILURE; } RIOT_STATUS DERDECGetPubKeyFromPrivKey (const uint8_t **public_key, size_t *key_len, const uint8_t *private_key_der, const size_t key_der_len) { size_t position = 0; size_t len; if ((public_key == NULL) || (private_key_der == NULL)) { goto Error; } ASRT (key_der_len <= RIOT_X509_MAX_KEY_LEN); CHK (read_sequence (NULL, private_key_der, key_der_len, &position)); CHK (read_integer (private_key_der, key_der_len, &position)); CHK (read_octet_string (&len, private_key_der, key_der_len, &position)); position += len; CHK (read_explicit_type (private_key_der, key_der_len, &position, 0)); CHK (decode_explicit_bit_string (public_key, key_len, private_key_der, key_der_len, &position, 1)); return RIOT_SUCCESS; Error: return RIOT_FAILURE; } RIOT_STATUS DERDECGetSubjectName (char **name, const uint8_t *der, const size_t length) { size_t position = 0; size_t len; int status; if ((name == NULL) || (der == NULL)) { goto Error; } *name = NULL; status = read_cert_subject_name (&len, &position, der, length); if ((status != 0) || ((position + len) > length)) { goto Error; } *name = platform_malloc (len + 1); if (*name == NULL) { goto Error; } memcpy (*name, &der[position], len); (*name)[len] = '\0'; return RIOT_SUCCESS; Error: return RIOT_FAILURE; } RIOT_STATUS DERDECVerifyCert (const uint8_t *der, const size_t length) { size_t position = 0; size_t len; int status; ASRT (der != NULL); status = read_cert_subject_name (&len, &position, der, length); if ((status != 0) || ((position + len) > length)) { goto Error; } return RIOT_SUCCESS; Error: return RIOT_FAILURE; }