core/asn1/ecc_der_util.c (579 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <string.h>
#include "asn1_util.h"
#include "ecc_der_util.h"
/**
* The ASN.1 encoded OID for the P256 ECC curve.
*/
static const uint8_t ECC_DER_P256_OID[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07
};
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_384
/**
* The ASN.1 encoded OID for the P384 ECC curve.
*/
static const uint8_t ECC_DER_P384_OID[] = {
0x2b, 0x81, 0x04, 0x00, 0x22
};
#endif
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
/**
* The ASN.1 encoded OID for the P521 ECC curve.
*/
static const uint8_t ECC_DER_P521_OID[] = {
0x2b, 0x81, 0x04, 0x00, 0x23
};
#endif
/**
* The ASN.1 encoded OID for an ECC public key.
*/
static const uint8_t ECC_DER_EC_PUBLIC_KEY_OID[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01
};
/**
* Get the next tag in an ASN.1 buffer and determine basic information about it.
*
* @param type The expected type for the next tag in the buffer.
* @param der The ASN.1 buffer to parse. This must be pointing to the start of a new tag. Upon
* returning, this will be moved past the tag header bytes.
* @param length The total length remaining in the data buffer. Upon output, this will be updated
* based on the length of the header.
* @param type_len Output for the data length described by the tag.
*
* @return 0 if the next tag was successfully parsed or an error code.
*/
static int ecc_der_get_next_tag (uint8_t type, const uint8_t **der, size_t *length,
size_t *type_len)
{
const uint8_t *pos = *der;
uint8_t header_len;
if (*length < 3) {
/* While 2 bytes is enough for the short length representation, we will always have more
* data overall after a tag, so even in the case of a 2 byte header, not enough space for
* more data is an error. Just check the length once here to avoid needing repeated length
* checks. */
return ECC_DER_UTIL_MALFORMED;
}
if (pos[0] != type) {
return ECC_DER_UTIL_UNEXPECTED_TAG;
}
if (pos[1] < 0x80) {
*type_len = pos[1];
header_len = 2;
}
else if (pos[1] == 0x81) {
*type_len = pos[2];
header_len = 3;
}
else {
/* We will never get an ASN.1 sequence that needs more than a single length byte. If we do,
* there is no point parsing it any further since it does not represent an ECC key.
*
* This same error will trigger if any sub-tag has more than one length byte. In that case,
* the encoding is technically malformed. */
return ECC_DER_UTIL_UNKNOWN_SEQUENCE;
}
if (*length < (header_len + *type_len)) {
return ECC_DER_UTIL_MALFORMED;
}
*der = pos + header_len;
*length -= header_len;
return 0;
}
/**
* Add the next tag in an ASN.1 buffer.
*
* @param type The type for the next tag to be added in the buffer.
* @param type_len Data length to specify in the tag.
* @param data Buffer for the data to add to the tag. Set to null if no data should be added.
* @param der Input the current position in the ASN.1 buffer. Output the buffer position after the
* tag has been added.
* @param length Input the length of the ASN.1 buffer. Output the remaining buffer length after the
* tag has been added.
*
* @return 0 if the tag was added successfully or an error code.
*/
static int ecc_der_add_next_tag (uint8_t type, size_t type_len, const uint8_t *data, uint8_t **der,
size_t *length)
{
uint8_t *pos = *der;
if (*length < 3) {
/* Technically we may be able to fit a 2 byte header, but there is no scenario where a 2
* byte header would not be followed by another byte. So, just check against 3 bytes here
* to remove the need for additional checks. */
return ECC_DER_UTIL_SMALL_DER_BUFFER;
}
*pos++ = type;
if (type_len >= 0x80) {
*pos++ = 0x81;
(*length)--;
}
*pos++ = type_len;
*length -= 2;
if (data) {
if (*length < type_len) {
return ECC_DER_UTIL_SMALL_DER_BUFFER;
}
memcpy (pos, data, type_len);
pos += type_len;
*length -= type_len;
}
*der = pos;
return 0;
}
/*
* ECC private key ASN.1 structure: https://www.secg.org/sec1-v2.pdf
*
* CPrivateKey ::= SEQUENCE {
* version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
* privateKey OCTET STRING,
* parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
* publicKey [1] BIT STRING OPTIONAL
* }
*
* parameters and publicKey are EXPLICIT.
*/
/**
* Encode the public key into an ASN.1 BIT STRING.
*
* @param pub_key_x X coordinate of the ECC public key.
* @param pub_key_y Y coordinate of the ECC public key.
* @param key_length Length of the ECC key.
* @param der Output buffer for the encoded BIT STRING. The buffer pointer will be moved to the end
* of the encoded value.
* @param length Length of the output buffer. This will be updated on output based on the length of
* the encoded value.
*
* @return 0 if the BIT STRING was successfully encoded or an error code.
*/
static int ecc_der_encode_public_key_bit_string (const uint8_t *pub_key_x, const uint8_t *pub_key_y,
size_t key_length, uint8_t **der, size_t *length)
{
uint8_t *pos = *der;
uint8_t pub_key_hdr[] = {0x00, 0x04};
int status;
status = ecc_der_add_next_tag (0x03, (key_length * 2) + 2, NULL, &pos, length);
if (status != 0) {
return status;
}
if (*length < (sizeof (pub_key_hdr) + (key_length * 2))) {
return ECC_DER_UTIL_SMALL_DER_BUFFER;
}
memcpy (pos, pub_key_hdr, sizeof (pub_key_hdr));
pos += sizeof (pub_key_hdr);
memcpy (pos, pub_key_x, key_length);
pos += key_length;
memcpy (pos, pub_key_y, key_length);
pos += key_length;
*der = pos;
*length -= (sizeof (pub_key_hdr) + (key_length * 2));
return 0;
}
/**
* Extract an ECC private key from ASN.1/DER encoded data. If the DER data also contains a public
* key, this value is ignored.
*
* Only P256, P384, and P521 curves are supported.
*
* @param der An ASN.1/DER encoded ECC private key.
* @param length Length of the DER data.
* @param priv_key Output buffer for the raw private key.
* @param key_length Length of the private key buffer. The actual key length is determined by the
* encoded data.
*
* @return Length of the private key or an error code.
*/
int ecc_der_decode_private_key (const uint8_t *der, size_t length, uint8_t *priv_key,
size_t key_length)
{
const uint8_t *pos;
int type_len;
int key_len;
if ((der == NULL) || (priv_key == NULL)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
type_len = ecc_der_decode_private_key_no_copy (der, length, &pos);
if (ROT_IS_ERROR (type_len)) {
return type_len;
}
key_len = type_len;
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
if (type_len == (ECC_KEY_LENGTH_521 - 1)) {
/* Adjust key length for encoders that drop the first byte when it is 0. */
key_len++;
}
#endif
if (key_length >= (size_t) key_len) {
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
if (type_len == (ECC_KEY_LENGTH_521 - 1)) {
priv_key[0] = 0;
memcpy (&priv_key[1], pos, type_len);
}
else
#endif
{
memcpy (priv_key, pos, key_len);
}
}
else {
return ECC_DER_UTIL_SMALL_KEY_BUFFER;
}
return key_len;
}
/**
* Get a reference to an ECC private key within ASN.1/DER encoded data. If the DER data also
* contains a public key, this value is ignored.
*
* Only P256, P384, and P521 curves are supported.
*
* For P521 keys, the private key value may contain one less byte than expected, depending on the
* value of the private key and how the DER is encoded.
*
* @param der An ASN.1/DER encoded ECC private key.
* @param length Length of the DER data.
* @param priv_key Output for the location of the private key in the encoded data. The data is not
* copied, so it will only be valid as long as the encoded DER data remains valid.
*
* @return Length of the private key or an error code.
*/
int ecc_der_decode_private_key_no_copy (const uint8_t *der, size_t length, const uint8_t **priv_key)
{
const uint8_t *pos = der;
size_t type_len;
int key_len;
size_t oid_len;
const uint8_t *oid;
int status;
if ((der == NULL) || (priv_key == NULL)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
status = ecc_der_get_next_tag (0x30, &pos, &length, &type_len);
if (status != 0) {
return status;
}
status = ecc_der_get_next_tag (0x02, &pos, &length, &type_len);
if (status != 0) {
return status;
}
if ((type_len != 1) || (*pos != 1)) {
/* If this is not a version 1 structure, we don't know what it is. */
return ECC_DER_UTIL_UNKNOWN_SEQUENCE;
}
pos += type_len;
length -= type_len;
status = ecc_der_get_next_tag (0x04, &pos, &length, &type_len);
if (status != 0) {
return status;
}
/* Make sure the private key is a supported key length. */
switch (type_len) {
case ECC_KEY_LENGTH_256 + 1:
/* Some encoders add an extra zero byte if the MSB of the private key is 1, in the same
* way that integers need to be padded so they don't get interpreted as negative. */
if (*pos != 0) {
return ECC_DER_UTIL_UNSUPPORTED_KEY_LENGTH;
}
pos++;
type_len--;
/* fall through */ /* no break */
case ECC_KEY_LENGTH_256:
oid = ECC_DER_P256_OID;
oid_len = sizeof (ECC_DER_P256_OID);
break;
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_384
case ECC_KEY_LENGTH_384 + 1:
/* The same zero byte can exist for P384 private keys. */
if (*pos != 0) {
return ECC_DER_UTIL_UNSUPPORTED_KEY_LENGTH;
}
pos++;
type_len--;
/* fall through */ /* no break */
case ECC_KEY_LENGTH_384:
oid = ECC_DER_P384_OID;
oid_len = sizeof (ECC_DER_P384_OID);
break;
#endif
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
case ECC_KEY_LENGTH_521 - 1:
/* Some encoders drop the first byte of private key if it is 0, so accept one less byte
* than normally expected. */
case ECC_KEY_LENGTH_521:
oid = ECC_DER_P521_OID;
oid_len = sizeof (ECC_DER_P521_OID);
break;
#endif
default:
return ECC_DER_UTIL_UNSUPPORTED_KEY_LENGTH;
}
key_len = type_len;
*priv_key = pos;
/* We have the private key of a valid length, but we need to make sure it is for the expected
* curve. While this field is defined as optional, it is expected to always be present, and
* RFC 5915 mandates this. */
pos += type_len;
length -= type_len;
status = ecc_der_get_next_tag (0xa0, &pos, &length, &type_len);
if (status != 0) {
return status;
}
status = ecc_der_get_next_tag (0x06, &pos, &length, &type_len);
if (status != 0) {
return status;
}
if ((type_len != oid_len) || (memcmp (pos, oid, oid_len) != 0)) {
/* Only a single curve is supported for each key length. If the OID indicates a different
* curve, report the key as unsupported. */
return ECC_DER_UTIL_UNSUPPORTED_CURVE;
}
return key_len;
}
/**
* Encode an ECC private key using ASN.1/DER.
*
* Only P256, P384, and P521 curves are supported.
*
* @param priv_key The private key to encode.
* @param pub_key_x Optional X coordinate for the ECC public key. If this is null, the public key
* will not be encoded.
* @param pub_key_y Optional Y coordinate for the ECC public key. If this is null, the public key
* will not be encoded.
* @param key_length Length of the private key. All key buffers must be the same size.
* @param der Output buffer for the DER encoded private key. This will include the optional public
* key portion if that information was provided.
* @param length Length of the DER output buffer. This must be large enough to hold all key
* information and the encoding overhead.
*
* @return Length of the encoded data or an error code.
*/
int ecc_der_encode_private_key (const uint8_t *priv_key, const uint8_t *pub_key_x,
const uint8_t *pub_key_y, size_t key_length, uint8_t *der, size_t length)
{
uint8_t *pos = der;
uint8_t has_pub_key = 0;
uint8_t seq_hdr_len = 2;
uint8_t version = 1;
size_t oid_len;
const uint8_t *oid;
int status;
if ((priv_key == NULL) || (der == NULL)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_384
if (pub_key_x && pub_key_y && (key_length > ECC_KEY_LENGTH_256)) {
has_pub_key = 0x80;
seq_hdr_len = 3;
}
#endif
switch (key_length) {
case ECC_KEY_LENGTH_256:
oid_len = sizeof (ECC_DER_P256_OID);
oid = ECC_DER_P256_OID;
status = ecc_der_add_next_tag (0x30, has_pub_key, NULL, &pos, &length);
break;
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_384
case ECC_KEY_LENGTH_384:
oid_len = sizeof (ECC_DER_P384_OID);
oid = ECC_DER_P384_OID;
status = ecc_der_add_next_tag (0x30, has_pub_key, NULL, &pos, &length);
break;
#endif
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
case ECC_KEY_LENGTH_521:
oid_len = sizeof (ECC_DER_P521_OID);
oid = ECC_DER_P521_OID;
status = ecc_der_add_next_tag (0x30, has_pub_key, NULL, &pos, &length);
break;
#endif
default:
return ECC_DER_UTIL_UNSUPPORTED_KEY_LENGTH;
}
if (status != 0) {
return status;
}
status = ecc_der_add_next_tag (0x02, 1, &version, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_add_next_tag (0x04, key_length, priv_key, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_add_next_tag (0xa0, oid_len + 2, NULL, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_add_next_tag (0x06, oid_len, oid, &pos, &length);
if (status != 0) {
return status;
}
if (pub_key_x && pub_key_y) {
if (key_length != ECC_KEY_LENGTH_521) {
status = ecc_der_add_next_tag (0xa1, (key_length * 2) + 4, NULL, &pos, &length);
}
else {
status = ecc_der_add_next_tag (0xa1, (key_length * 2) + 5, NULL, &pos, &length);
}
if (status != 0) {
return status;
}
status = ecc_der_encode_public_key_bit_string (pub_key_x, pub_key_y, key_length, &pos,
&length);
if (status != 0) {
return status;
}
}
der[seq_hdr_len - 1] = (pos - der) - seq_hdr_len;
return der[seq_hdr_len - 1] + seq_hdr_len;
}
/**
* Inspect a DER encoded ECC private key to determine the total length of the key 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 encoded length is less than or equal to the buffer 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 An ASN.1/DER encoded ECC private key.
* @param max_length Length of the data buffer containing the key.
*
* @return Length of the key data contained within the buffer.
*/
size_t ecc_der_get_private_key_length (const uint8_t *der, size_t max_length)
{
return asn1_get_der_encoded_length (der, max_length);
}
/*
* ECC public key ASN.1 structure: https://datatracker.ietf.org/doc/html/rfc5480
*
*
* SubjectPublicKeyInfo ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING
* }
*
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
*
* parameters is used to specify the OID for the named curve of the key.
* subjectPublicKey is the ECPoint for the public key.
*/
/**
* Extract an ECC public key from ASN.1/DER encoded data.
*
* Only P256, P384, and P521 curves are supported. Only uncompressed public keys will be decoded.
*
* @param der An ASN.1/DER encoded ECC public key.
* @param length Length of the DER data.
* @param pub_key_x Output buffer for the X coordinate of the public key.
* @param pub_key_y Output buffer for the Y coordinate of the public key.
* @param key_length Length of the public key buffers. Each component of the public key must be the
* same length. The actual key length is determined by the encoded data.
*
* @return Length of the ECC key or an error code.
*/
int ecc_der_decode_public_key (const uint8_t *der, size_t length, uint8_t *pub_key_x,
uint8_t *pub_key_y, size_t key_length)
{
const uint8_t *pos;
int type_len;
size_t key_len;
if ((der == NULL) || (pub_key_x == NULL) || (pub_key_y == NULL)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
type_len = ecc_der_decode_public_key_no_copy (der, length, &pos);
if (ROT_IS_ERROR (type_len)) {
return type_len;
}
/* Only uncompressed ECPoint values are supported. */
if (pos[0] != 0x04) {
return ECC_DER_UTIL_COMPRESSED_ECPOINT;
}
/* Skip the public key format byte. */
pos++;
key_len = (type_len - 1) / 2;
if (key_length < key_len) {
return ECC_DER_UTIL_SMALL_KEY_BUFFER;
}
memcpy (pub_key_x, pos, key_len);
memcpy (pub_key_y, &pos[key_len], key_len);
return key_len;
}
/**
* Get a reference to an ECC public key within ASN.1/DER encoded data. This will include the
* leading byte indicating how the public key is stored.
*
* Only P256, P384, and P521 curves are supported.
*
* @param der An ASN.1/DER encoded ECC public key.
* @param length Length of the DER data.
* @param pub_key Output for the location of the public key in the encoded data. The data is not
* copied, so it will only be valid as long as the encoded DER data remains valid.
*
* @return Length of the public key data or an error code.
*/
int ecc_der_decode_public_key_no_copy (const uint8_t *der, size_t length, const uint8_t **pub_key)
{
const uint8_t *pos = der;
size_t type_len;
size_t oid_len;
size_t key_len;
const uint8_t *oid;
int status;
if ((der == NULL) || (pub_key == NULL)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
status = ecc_der_get_next_tag (0x30, &pos, &length, &type_len);
if (status != 0) {
return status;
}
status = ecc_der_get_next_tag (0x30, &pos, &length, &type_len);
if (status != 0) {
return status;
}
status = ecc_der_get_next_tag (0x06, &pos, &length, &type_len);
if (status != 0) {
return status;
}
if (((type_len != sizeof (ECC_DER_EC_PUBLIC_KEY_OID)) ||
(memcmp (pos, ECC_DER_EC_PUBLIC_KEY_OID, type_len) != 0))) {
return ECC_DER_UTIL_UNSUPPORTED_ALGORITHM;
}
pos += type_len;
length -= type_len;
status = ecc_der_get_next_tag (0x06, &pos, &length, &type_len);
if (status != 0) {
return status;
}
/* Save the curve OID so we can compare against it once we know the key length. */
oid_len = type_len;
oid = pos;
pos += type_len;
length -= type_len;
status = ecc_der_get_next_tag (0x03, &pos, &length, &type_len);
if (status != 0) {
return status;
}
key_len = (type_len - 2) / 2;
switch (key_len) {
case ECC_KEY_LENGTH_256:
if ((oid_len != sizeof (ECC_DER_P256_OID)) ||
(memcmp (oid, ECC_DER_P256_OID, oid_len) != 0)) {
return ECC_DER_UTIL_UNSUPPORTED_CURVE;
}
break;
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_384
case ECC_KEY_LENGTH_384:
if ((oid_len != sizeof (ECC_DER_P384_OID)) ||
(memcmp (oid, ECC_DER_P384_OID, oid_len) != 0)) {
return ECC_DER_UTIL_UNSUPPORTED_CURVE;
}
break;
#endif
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
case ECC_KEY_LENGTH_521:
if ((oid_len != sizeof (ECC_DER_P521_OID)) ||
(memcmp (oid, ECC_DER_P521_OID, oid_len) != 0)) {
return ECC_DER_UTIL_UNSUPPORTED_CURVE;
}
break;
#endif
default:
return ECC_DER_UTIL_UNSUPPORTED_KEY_LENGTH;
}
/* Skip over the leading zero in the public key bit string. */
pos++;
type_len--;
/* Confirm a valid ECPoint format for the public key. */
switch (pos[0]) {
case 0x02:
case 0x03:
/* Compressed ECPoint */
break;
case 0x04:
/* Uncompressed ECPoint */
break;
default:
return ECC_DER_UTIL_INVALID_ECPOINT;
}
*pub_key = pos;
return type_len;
}
/**
* Encode an ECC public key using ASN.1/DER.
*
* Only P256, P384, and P521 curves are supported.
*
* @param pub_key_x X coordinate of the ECC public key.
* @param pub_key_y Y coordinate of the ECC public key.
* @param key_length Length of the ECC key. Each component of the public key must be the same
* length.
* @param der Output buffer for the DER encoded public key.
* @param length Length of the DER output buffer. This must be large enough to hold both public key
* components and the encoding overhead.
*
* @return Length of the encoded data or an error code.
*/
int ecc_der_encode_public_key (const uint8_t *pub_key_x, const uint8_t *pub_key_y,
size_t key_length, uint8_t *der, size_t length)
{
uint8_t *pos = der;
uint8_t seq_hdr_len = 2;
size_t algo_len;
size_t oid_len;
const uint8_t *oid;
int status;
if ((pub_key_x == NULL) || (pub_key_y == NULL) || (der == NULL)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
switch (key_length) {
case ECC_KEY_LENGTH_256:
algo_len = 0x13;
oid = ECC_DER_P256_OID;
oid_len = sizeof (ECC_DER_P256_OID);
break;
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_384
case ECC_KEY_LENGTH_384:
algo_len = 0x10;
oid = ECC_DER_P384_OID;
oid_len = sizeof (ECC_DER_P384_OID);
break;
#endif
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
case ECC_KEY_LENGTH_521:
seq_hdr_len = 3;
algo_len = 0x10;
oid = ECC_DER_P521_OID;
oid_len = sizeof (ECC_DER_P521_OID);
break;
#endif
default:
return ECC_DER_UTIL_UNSUPPORTED_KEY_LENGTH;
}
status = ecc_der_add_next_tag (0x30, (seq_hdr_len == 2) ? 0 : 0x80, NULL, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_add_next_tag (0x30, algo_len, NULL, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_add_next_tag (0x06, sizeof (ECC_DER_EC_PUBLIC_KEY_OID),
ECC_DER_EC_PUBLIC_KEY_OID, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_add_next_tag (0x06, oid_len, oid, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_encode_public_key_bit_string (pub_key_x, pub_key_y, key_length, &pos, &length);
if (status != 0) {
return status;
}
der[seq_hdr_len - 1] = (pos - der) - seq_hdr_len;
return der[seq_hdr_len - 1] + seq_hdr_len;
}
/**
* Inspect a DER encoded ECC public key to determine the total length of the key 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 encoded length is less than or equal to the buffer 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 An ASN.1/DER encoded ECC public key.
* @param max_length Length of the data buffer containing the key.
*
* @return Length of the key data contained within the buffer.
*/
size_t ecc_der_get_public_key_length (const uint8_t *der, size_t max_length)
{
return asn1_get_der_encoded_length (der, max_length);
}
/*
* ECDSA signature ASN.1 structure: https://datatracker.ietf.org/doc/html/rfc3279
*
* Ecdsa-Sig-Value ::= SEQUENCE {
* r INTEGER,
* s INTEGER }
*/
/**
* Decode a single INTEGER from an ASN.1 encoded ECDSA signature.
*
* @param der The INTEGER to decode. On output, this will be updated to point past the decoded
* data.
* @param length Total length of the encoded data. Upon return, this will be updated to reflect the
* remaining data after the decoded INTEGER.
* @param sig Output buffer for the INTEGER value.
* @param key_length Length of the INTEGER. Encoded values that are smaller than this will have the
* MSBs zero padded.
*
* @return 0 if the INTEGER was successfully decoded or an error code.
*/
static int ecc_der_decode_ecdsa_integer (const uint8_t **der, size_t *length, uint8_t *sig,
size_t key_length)
{
size_t type_len;
int status;
status = ecc_der_get_next_tag (0x02, der, length, &type_len);
if (status != 0) {
return status;
}
if (type_len > (key_length + 1)) {
/* A signature can have an additional byte of zero padding to account for negative numbers,
* any more than that means this signature does not match the provided key length. */
return ECC_DER_UTIL_SIG_TOO_LONG;
}
if (type_len > key_length) {
if (**der != 0) {
/* The extra byte is not zero, which means the integer is too long for the key size. */
return ECC_DER_UTIL_SIG_TOO_LONG;
}
(*der)++;
(*length)--;
type_len--;
}
memset (sig, 0, key_length);
memcpy (&sig[key_length - type_len], *der, type_len);
*der += type_len;
*length -= type_len;
return 0;
}
/**
* Encode a single ECDSA signature value to an ASN.1 INTEGER.
*
* @param sig The signature value to encode.
* @param key_length Length of the value.
* @param der Output buffer for the encoded INTEGER. The buffer pointer will be moved to the end of
* the encoded value.
* @param length Length of the output buffer. This will be updated on output based on the length of
* the encoded value.
*
* @return 0 if the signature value was encoded successfully or an error code.
*/
static int ecc_der_encode_ecdsa_integer (const uint8_t *sig, size_t key_length, uint8_t **der,
size_t *length)
{
uint8_t *pos = *der;
uint8_t *int_len = &(*der)[1];
size_t i = 0;
int status;
status = ecc_der_add_next_tag (0x02, 0, NULL, &pos, length);
if (status != 0) {
return status;
}
/* Trim leading zeros from the signature value. */
while ((sig[i] == 0) && (key_length > 1)) {
i++;
key_length--;
}
/* Pad with an extra zero to avoid negative numbers in the encoding. There will always be
* enough buffer space for this padding because of the length check when adding the tag. */
if (sig[i] & 0x80) {
*pos++ = 0;
(*length)--;
(*int_len)++;
}
if (*length < key_length) {
return ECC_DER_UTIL_SMALL_DER_BUFFER;
}
memcpy (pos, &sig[i], key_length);
pos += key_length;
*length -= key_length;
*int_len += key_length;
*der = pos;
return 0;
}
/**
* Extract an ECDSA signature from ASN.1/DER encoded data. Any additional bytes in the data buffer
* beyond the DER encoded length will be ignored.
*
* @param der An ASN.1/DER encoded ECDSA signature.
* @param length Length of the DER data.
* @param sig_r Output buffer for the r value of the signature.
* @param sig_s Output buffer for the s value of the signature.
* @param key_length The size of the key used to generate the signature. The signature output
* buffers must each be able to hold this much data. The encoded data cannot be used to reliably
* determine the key length since INTEGER encoding adds and removes leading zeros.
*
* @return 0 if the signature was parsed successfully or an error code.
*/
int ecc_der_decode_ecdsa_signature (const uint8_t *der, size_t length, uint8_t *sig_r,
uint8_t *sig_s, size_t key_length)
{
const uint8_t *pos = der;
size_t type_len;
int status;
if ((der == NULL) || (sig_r == NULL) || (sig_s == NULL)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
status = ecc_der_get_next_tag (0x30, &pos, &length, &type_len);
if (status != 0) {
return status;
}
status = ecc_der_decode_ecdsa_integer (&pos, &length, sig_r, key_length);
if (status != 0) {
return status;
}
return ecc_der_decode_ecdsa_integer (&pos, &length, sig_s, key_length);
}
/**
* Encode an ECDSA signature using ASN.1/DER.
*
* @param sig_r r value for the signature.
* @param sig_s s value for the signature.
* @param key_length Length of the signature components. Both components must be the same length.
* @param der Output buffer for the DER encoded signature.
* @param length Length of the DER output buffer. This must be large enough to hold both signature
* components and the encoding overhead.
*
* @return Length of the encoded signature or an error code.
*/
int ecc_der_encode_ecdsa_signature (const uint8_t *sig_r, const uint8_t *sig_s, size_t key_length,
uint8_t *der, size_t length)
{
uint8_t *pos = der;
int total_len;
int status;
if ((sig_r == NULL) || (sig_s == NULL) || (der == NULL) || (key_length == 0)) {
return ECC_DER_UTIL_INVALID_ARGUMENT;
}
status = ecc_der_add_next_tag (0x30, 0, NULL, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_encode_ecdsa_integer (sig_r, key_length, &pos, &length);
if (status != 0) {
return status;
}
status = ecc_der_encode_ecdsa_integer (sig_s, key_length, &pos, &length);
if (status != 0) {
return status;
}
total_len = (pos - der) - 2;
der[1] = total_len;
#if ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521
/* For P521 signatures, the resulting structure could require 3 header bytes. Adjust here, if
* necessary. */
if (total_len >= 0x80) {
if (length == 0) {
return ECC_DER_UTIL_SMALL_DER_BUFFER;
}
memmove (&der[2], &der[1], total_len + 1);
der[1] = 0x81;
total_len++;
}
#endif
return total_len + 2;
}
/**
* Inspect a DER encoded ECDSA signature to determine the total length of the signature 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 encoded length is less than or equal to the buffer 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 An ASN.1/DER encoded ECDSA signature.
* @param max_length Length of the data buffer containing the signature.
*
* @return Length of the signature data contained within the buffer.
*/
size_t ecc_der_get_ecdsa_signature_length (const uint8_t *der, size_t max_length)
{
return asn1_get_der_encoded_length (der, max_length);
}