| /* |
| * 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); |
| } |