mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-01-30 15:32:58 +00:00
psa_util: add raw<->DER ECDSA conversion functions
Signed-off-by: Valerio Setti <valerio.setti@nordicsemi.no>
This commit is contained in:
parent
aa3fa98bc4
commit
75501f5ede
@ -176,6 +176,42 @@ static inline mbedtls_md_type_t mbedtls_md_type_from_psa_alg(psa_algorithm_t psa
|
||||
return (mbedtls_md_type_t) (psa_alg & PSA_ALG_HASH_MASK);
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_ASN1_WRITE_C)
|
||||
/** Convert an ECDSA signature from raw format to DER ASN.1 one.
|
||||
*
|
||||
* \param raw Buffer that contains the signature in raw format.
|
||||
* \param raw_len Length of raw buffer in bytes
|
||||
* \param[out] der Buffer that will be filled with the converted DER
|
||||
* output. It can overlap with raw buffer.
|
||||
* \param der_size Size of the output der buffer in bytes.
|
||||
* \param[out] der_len On success it contains the amount of valid data
|
||||
* (in bytes) written to der buffer. It's undefined
|
||||
* in case of failure.
|
||||
* \param bits Size of each raw coordinate in bits.
|
||||
*/
|
||||
int mbedtls_ecdsa_raw_to_der(const unsigned char *raw, size_t raw_len,
|
||||
unsigned char *der, size_t der_size, size_t *der_len,
|
||||
size_t bits);
|
||||
#endif /* MBEDTLS_ASN1_WRITE_C */
|
||||
|
||||
#if defined(MBEDTLS_ASN1_PARSE_C)
|
||||
/** Convert an ECDSA signature from DER ASN.1 format to raw.
|
||||
*
|
||||
* \param der Buffer that contains the signature in DER format.
|
||||
* \param der_len Size of the der buffer in bytes.
|
||||
* \param[out] raw Buffer that will be filled with the converted raw
|
||||
* signature. It can overlap with der buffer.
|
||||
* \param raw_size Size of the raw buffer in bytes.
|
||||
* \param[out] raw_len On success it is updated with the amount of valid
|
||||
* data (in bytes) written to raw buffer. It's undefined
|
||||
* in case of failure.
|
||||
* \param bits Size of each raw coordinate in bits.
|
||||
*/
|
||||
int mbedtls_ecdsa_der_to_raw(const unsigned char *der, size_t der_len,
|
||||
unsigned char *raw, size_t raw_size, size_t *raw_len,
|
||||
size_t bits);
|
||||
#endif /* MBEDTLS_ASN1_PARSE_C */
|
||||
|
||||
/**@}*/
|
||||
|
||||
#endif /* MBEDTLS_PSA_CRYPTO_C */
|
||||
|
@ -40,6 +40,10 @@
|
||||
#if defined(MBEDTLS_BLOCK_CIPHER_SOME_PSA)
|
||||
#include <mbedtls/cipher.h>
|
||||
#endif
|
||||
#if defined(MBEDTLS_ASN1_WRITE_C)
|
||||
#include <mbedtls/asn1write.h>
|
||||
#include <psa/crypto_sizes.h>
|
||||
#endif
|
||||
|
||||
/* PSA_SUCCESS is kept at the top of each error table since
|
||||
* it's the most common status when everything functions properly. */
|
||||
@ -330,4 +334,205 @@ mbedtls_ecp_group_id mbedtls_ecc_group_from_psa(psa_ecc_family_t family,
|
||||
}
|
||||
#endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
|
||||
|
||||
#if defined(MBEDTLS_ASN1_WRITE_C)
|
||||
/*
|
||||
* Convert a single raw coordinate to DER ASN.1 format.
|
||||
* Note: this function is similar to mbedtls_asn1_write_mpi(), but it doesn't
|
||||
* depend on BIGNUM_C.
|
||||
* Note: this function fills der_buf backward.
|
||||
*/
|
||||
static int convert_raw_to_der_single_int(const unsigned char *raw_buf, size_t raw_len,
|
||||
unsigned char *der_buf_start,
|
||||
unsigned char *der_buf_end)
|
||||
{
|
||||
unsigned char *p = der_buf_end;
|
||||
int len = raw_len;
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
|
||||
/* Copy the raw coordinate to the end of der_buf. */
|
||||
if ((p - der_buf_start) < len) {
|
||||
return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
|
||||
}
|
||||
p -= len;
|
||||
memcpy(p, raw_buf, len);
|
||||
|
||||
/* ASN.1 DER encoding requires minimal length, so skip leading 0s.
|
||||
* Provided input MPIs should not be 0, but as a failsafe measure, still
|
||||
* detect that and return error in case. */
|
||||
while (*p == 0x00) {
|
||||
++p;
|
||||
--len;
|
||||
if (len == 0) {
|
||||
return MBEDTLS_ERR_ASN1_INVALID_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
/* If MSb is 1, ASN.1 requires that we prepend a 0. */
|
||||
if (*p & 0x80) {
|
||||
if ((p - der_buf_start) < 1) {
|
||||
return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
|
||||
}
|
||||
--p;
|
||||
*p = 0x00;
|
||||
++len;
|
||||
}
|
||||
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der_buf_start, len));
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der_buf_start, MBEDTLS_ASN1_INTEGER));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int mbedtls_ecdsa_raw_to_der(const unsigned char *raw, size_t raw_len,
|
||||
unsigned char *der, size_t der_size, size_t *der_len,
|
||||
size_t bits)
|
||||
{
|
||||
unsigned char r[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
|
||||
unsigned char s[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
|
||||
const size_t coordinate_len = PSA_BITS_TO_BYTES(bits);
|
||||
size_t len = 0;
|
||||
unsigned char *p = der + der_size;
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
|
||||
if (raw_len < 2 * coordinate_len) {
|
||||
return MBEDTLS_ERR_ASN1_INVALID_DATA;
|
||||
}
|
||||
|
||||
/* Since raw and der buffers might overlap, dump r and s before starting
|
||||
* the conversion. */
|
||||
memset(r, 0, sizeof(r));
|
||||
memcpy(r, raw, coordinate_len);
|
||||
memset(s, 0, sizeof(s));
|
||||
memcpy(s, raw + coordinate_len, coordinate_len);
|
||||
|
||||
/* der buffer will initially be written starting from its end so we pick s
|
||||
* first and then r. */
|
||||
ret = convert_raw_to_der_single_int(s, coordinate_len, der, p);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
p -= ret;
|
||||
len += ret;
|
||||
|
||||
ret = convert_raw_to_der_single_int(r, coordinate_len, der, p);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
p -= ret;
|
||||
len += ret;
|
||||
|
||||
/* Add ASN.1 header (len + tag). */
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der, len));
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der,
|
||||
MBEDTLS_ASN1_CONSTRUCTED |
|
||||
MBEDTLS_ASN1_SEQUENCE));
|
||||
|
||||
/* memmove the content of der buffer to its beginnig. */
|
||||
memmove(der, p, len);
|
||||
*der_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* MBEDTLS_ASN1_WRITE_C */
|
||||
|
||||
#if defined(MBEDTLS_ASN1_PARSE_C)
|
||||
/*
|
||||
* Convert a single integer from ASN.1 DER format to raw.
|
||||
* Note: der and raw buffers are not overlapping here.
|
||||
*/
|
||||
static int convert_der_to_raw_single_int(unsigned char *der, size_t der_len,
|
||||
unsigned char *raw, size_t raw_len,
|
||||
size_t coordinate_size)
|
||||
{
|
||||
unsigned char *p = der;
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
size_t unpadded_len, padding_len = 0;
|
||||
|
||||
/* Get the length of ASN.1 element (i.e. the integer we need to parse). */
|
||||
ret = mbedtls_asn1_get_tag(&p, p + der_len, &unpadded_len,
|
||||
MBEDTLS_ASN1_INTEGER);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Skip leading zeros */
|
||||
while (*p == 0x00) {
|
||||
p++;
|
||||
unpadded_len--;
|
||||
/* It should never happen that the input number is all zeros. */
|
||||
if (unpadded_len == 0) {
|
||||
return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (raw_len < coordinate_size) {
|
||||
return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
if (unpadded_len < coordinate_size) {
|
||||
padding_len = coordinate_size - unpadded_len;
|
||||
memset(raw, 0x00, padding_len);
|
||||
}
|
||||
memcpy(raw + padding_len, p, unpadded_len);
|
||||
p += unpadded_len;
|
||||
|
||||
return (int) (p - der);
|
||||
}
|
||||
|
||||
int mbedtls_ecdsa_der_to_raw(const unsigned char *der, size_t der_len,
|
||||
unsigned char *raw, size_t raw_size, size_t *raw_len,
|
||||
size_t bits)
|
||||
{
|
||||
unsigned char raw_tmp[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE];
|
||||
unsigned char *p = (unsigned char *) der;
|
||||
size_t data_len;
|
||||
size_t coordinate_size = PSA_BITS_TO_BYTES(bits);
|
||||
int ret;
|
||||
|
||||
/* The output raw buffer should be at least twice the size of a raw
|
||||
* coordinate in order to store r and s. */
|
||||
if (raw_size < coordinate_size * 2) {
|
||||
return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
|
||||
}
|
||||
|
||||
/* Check that the provided input DER buffer has the right header. */
|
||||
ret = mbedtls_asn1_get_tag(&p, der + der_len, &data_len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(raw_tmp, 0, sizeof(raw_tmp));
|
||||
|
||||
/* Extract r */
|
||||
ret = convert_der_to_raw_single_int(p, data_len, raw_tmp, sizeof(raw_tmp),
|
||||
coordinate_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
p += ret;
|
||||
data_len -= ret;
|
||||
|
||||
/* Extract s */
|
||||
ret = convert_der_to_raw_single_int(p, data_len, raw_tmp + coordinate_size,
|
||||
sizeof(raw_tmp) - coordinate_size,
|
||||
coordinate_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
p += ret;
|
||||
data_len -= ret;
|
||||
|
||||
/* Check that we consumed all the input der data. */
|
||||
if ((p - der) != (int) der_len) {
|
||||
return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
memcpy(raw, raw_tmp, 2 * coordinate_size);
|
||||
*raw_len = 2 * coordinate_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* MBEDTLS_ASN1_PARSE_C */
|
||||
|
||||
#endif /* MBEDTLS_PSA_CRYPTO_C */
|
||||
|
Loading…
x
Reference in New Issue
Block a user