/* * Public Key layer for parsing key files and structures * * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file is part of mbed TLS (https://tls.mbed.org) */ #include "pkcs11_mbedtls_utils.h" #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else #include MBEDTLS_CONFIG_FILE #endif #include "mbedtls/base64.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/entropy.h" #include "mbedtls/oid.h" #include "mbedtls/pk.h" #include "mbedtls/pk_internal.h" #include "mbedtls/sha256.h" #include "mbedtls/x509_crt.h" #include "string.h" #if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) /* * Parse a SpecifiedECDomain (SEC 1 C.2) and (mostly) fill the group with it. * WARNING: the resulting group should only be used with * pk_group_id_from_specified(), since its base point may not be set correctly * if it was encoded compressed. * * SpecifiedECDomain ::= SEQUENCE { * version SpecifiedECDomainVersion(ecdpVer1 | ecdpVer2 | ecdpVer3, ...), * fieldID FieldID {{FieldTypes}}, * curve Curve, * base ECPoint, * order INTEGER, * cofactor INTEGER OPTIONAL, * hash HashAlgorithm OPTIONAL, * ... * } * * We only support prime-field as field type, and ignore hash and cofactor. */ static int pk_group_from_specified(const mbedtls_asn1_buf *params, mbedtls_ecp_group *grp) { int ret; unsigned char *p = params->p; const unsigned char *const end = params->p + params->len; const unsigned char *end_field, *end_curve; size_t len; int ver; /* SpecifiedECDomainVersion ::= INTEGER { 1, 2, 3 } */ if ((ret = mbedtls_asn1_get_int(&p, end, &ver)) != 0) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret); if (ver < 1 || ver > 3) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT); /* * FieldID { FIELD-ID:IOSet } ::= SEQUENCE { -- Finite field * fieldType FIELD-ID.&id({IOSet}), * parameters FIELD-ID.&Type({IOSet}{@fieldType}) * } */ if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) return (ret); end_field = p + len; /* * FIELD-ID ::= TYPE-IDENTIFIER * FieldTypes FIELD-ID ::= { * { Prime-p IDENTIFIED BY prime-field } | * { Characteristic-two IDENTIFIED BY characteristic-two-field } * } * prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } */ if ((ret = mbedtls_asn1_get_tag(&p, end_field, &len, MBEDTLS_ASN1_OID)) != 0) return (ret); if (len != MBEDTLS_OID_SIZE(MBEDTLS_OID_ANSI_X9_62_PRIME_FIELD) || memcmp(p, MBEDTLS_OID_ANSI_X9_62_PRIME_FIELD, len) != 0) { return (MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE); } p += len; /* Prime-p ::= INTEGER -- Field of size p. */ if ((ret = mbedtls_asn1_get_mpi(&p, end_field, &grp->P)) != 0) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret); grp->pbits = mbedtls_mpi_bitlen(&grp->P); if (p != end_field) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); /* * Curve ::= SEQUENCE { * a FieldElement, * b FieldElement, * seed BIT STRING OPTIONAL * -- Shall be present if used in SpecifiedECDomain * -- with version equal to ecdpVer2 or ecdpVer3 * } */ if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) return (ret); end_curve = p + len; /* * FieldElement ::= OCTET STRING * containing an integer in the case of a prime field */ if ((ret = mbedtls_asn1_get_tag(&p, end_curve, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0 || (ret = mbedtls_mpi_read_binary(&grp->A, p, len)) != 0) { return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret); } p += len; if ((ret = mbedtls_asn1_get_tag(&p, end_curve, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0 || (ret = mbedtls_mpi_read_binary(&grp->B, p, len)) != 0) { return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret); } p += len; /* Ignore seed BIT STRING OPTIONAL */ if ((ret = mbedtls_asn1_get_tag(&p, end_curve, &len, MBEDTLS_ASN1_BIT_STRING)) == 0) p += len; if (p != end_curve) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); /* * ECPoint ::= OCTET STRING */ if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret); if ((ret = mbedtls_ecp_point_read_binary(grp, &grp->G, (const unsigned char *)p, len)) != 0) { /* * If we can't read the point because it's compressed, cheat by * reading only the X coordinate and the parity bit of Y. */ if (ret != MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE || (p[0] != 0x02 && p[0] != 0x03) || len != mbedtls_mpi_size(&grp->P) + 1 || mbedtls_mpi_read_binary(&grp->G.X, p + 1, len - 1) != 0 || mbedtls_mpi_lset(&grp->G.Y, p[0] - 2) != 0 || mbedtls_mpi_lset(&grp->G.Z, 1) != 0) { return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT); } } p += len; /* * order INTEGER */ if ((ret = mbedtls_asn1_get_mpi(&p, end, &grp->N)) != 0) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret); grp->nbits = mbedtls_mpi_bitlen(&grp->N); /* * Allow optional elements by purposefully not enforcing p == end here. */ return (0); } /* * Find the group id associated with an (almost filled) group as generated by * pk_group_from_specified(), or return an error if unknown. */ static int pk_group_id_from_group(const mbedtls_ecp_group *grp, mbedtls_ecp_group_id *grp_id) { int ret = 0; mbedtls_ecp_group ref; const mbedtls_ecp_group_id *id; mbedtls_ecp_group_init(&ref); for (id = mbedtls_ecp_grp_id_list(); *id != MBEDTLS_ECP_DP_NONE; id++) { /* Load the group associated to that id */ mbedtls_ecp_group_free(&ref); MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&ref, *id)); /* Compare to the group we were given, starting with easy tests */ if (grp->pbits == ref.pbits && grp->nbits == ref.nbits && mbedtls_mpi_cmp_mpi(&grp->P, &ref.P) == 0 && /*mbedtls_mpi_cmp_mpi( &grp->A, &ref.A ) == 0 &&*/ mbedtls_mpi_cmp_mpi(&grp->B, &ref.B) == 0 && mbedtls_mpi_cmp_mpi(&grp->N, &ref.N) == 0 && mbedtls_mpi_cmp_mpi(&grp->G.X, &ref.G.X) == 0 && mbedtls_mpi_cmp_mpi(&grp->G.Z, &ref.G.Z) == 0 && /* For Y we may only know the parity bit, so compare only that */ mbedtls_mpi_get_bit(&grp->G.Y, 0) == mbedtls_mpi_get_bit(&ref.G.Y, 0)) { break; } } cleanup: mbedtls_ecp_group_free(&ref); *grp_id = *id; if (ret == 0 && *id == MBEDTLS_ECP_DP_NONE) ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; return (ret); } /* * Parse a SpecifiedECDomain (SEC 1 C.2) and find the associated group ID */ static int pk_group_id_from_specified(const mbedtls_asn1_buf *params, mbedtls_ecp_group_id *grp_id) { int ret; mbedtls_ecp_group grp; mbedtls_ecp_group_init(&grp); if ((ret = pk_group_from_specified(params, &grp)) != 0) goto cleanup; ret = pk_group_id_from_group(&grp, grp_id); cleanup: mbedtls_ecp_group_free(&grp); return (ret); } #endif /* MBEDTLS_PK_PARSE_EC_EXTENDED */ /* * Use EC parameters to initialise an EC group * * ECParameters ::= CHOICE { * namedCurve OBJECT IDENTIFIER * specifiedCurve SpecifiedECDomain -- = SEQUENCE { ... } * -- implicitCurve NULL */ int pk_use_ecparams(const mbedtls_asn1_buf *params, mbedtls_ecp_group *grp) { int ret; mbedtls_ecp_group_id grp_id; if (params->tag == MBEDTLS_ASN1_OID) { if (mbedtls_oid_get_ec_grp(params, &grp_id) != 0) return (MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE); } else { #if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) if ((ret = pk_group_id_from_specified(params, &grp_id)) != 0) return (ret); #else return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT); #endif } /* * grp may already be initilialized; if so, make sure IDs match */ if (grp->id != MBEDTLS_ECP_DP_NONE && grp->id != grp_id) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT); if ((ret = mbedtls_ecp_group_load(grp, grp_id)) != 0) return (ret); return (0); } /* Minimally parse an ECParameters buffer to and mbedtls_asn1_buf * * ECParameters ::= CHOICE { * namedCurve OBJECT IDENTIFIER * specifiedCurve SpecifiedECDomain -- = SEQUENCE { ... } * -- implicitCurve NULL * } */ int pk_get_ecparams(unsigned char **p, const unsigned char *end, mbedtls_asn1_buf *params) { int ret; if (end - *p < 1) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + MBEDTLS_ERR_ASN1_OUT_OF_DATA); /* Tag may be either OID or SEQUENCE */ params->tag = **p; if (params->tag != MBEDTLS_ASN1_OID #if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) && params->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) #endif ) { return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); } if ((ret = mbedtls_asn1_get_tag(p, end, ¶ms->len, params->tag)) != 0) { return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + ret); } params->p = *p; *p += params->len; if (*p != end) return (MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); return (0); }