blob: 2b8a0578ec088295b060eb87c088b9759bba7744 [file] [log] [blame]
/*
* 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, &params->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);
}