/* * corePKCS11 v3.5.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * @file mbedtls_utils.c * @brief Helper functions originating from mbedTLS. */ /* Standard includes. */ #include /* mbedTLS includes. */ #include "mbedtls/base64.h" #include "mbedtls/rsa.h" #include "mbedtls/asn1.h" #include "mbedtls/platform_util.h" #include "mbedtls/oid.h" /*-----------------------------------------------------------*/ /* @brief Converts PEM documents into DER formatted byte arrays. * This is a helper function from mbedTLS util pem2der.c * (https://github.com/ARMmbed/mbedtls/blob/development/programs/util/pem2der.c#L75) * * \param pucInput[in] Pointer to PEM object * \param xLen[in] Length of PEM object * \param pucOutput[out] Pointer to buffer where DER oboject will be placed * \param pxOlen[in/out] Pointer to length of DER buffer. This value is updated * to contain the actual length of the converted DER object. * * \return 0 if successful. Negative if conversion failed. If buffer is not * large enough to hold converted object, pxOlen is still updated but -1 is returned. * */ int convert_pem_to_der( const unsigned char * pucInput, size_t xLen, unsigned char * pucOutput, size_t * pxOlen ) { int lRet; const unsigned char * pucS1; const unsigned char * pucS2; const unsigned char * pucEnd = pucInput + xLen; size_t xOtherLen = 0; pucS1 = ( unsigned char * ) strstr( ( const char * ) pucInput, "-----BEGIN" ); if( pucS1 == NULL ) { return( -1 ); } pucS2 = ( unsigned char * ) strstr( ( const char * ) pucInput, "-----END" ); if( pucS2 == NULL ) { return( -1 ); } pucS1 += 10; while( pucS1 < pucEnd && *pucS1 != '-' ) { pucS1++; } while( pucS1 < pucEnd && *pucS1 == '-' ) { pucS1++; } if( *pucS1 == '\r' ) { pucS1++; } if( *pucS1 == '\n' ) { pucS1++; } if( ( pucS2 <= pucS1 ) || ( pucS2 > pucEnd ) ) { return( -1 ); } lRet = mbedtls_base64_decode( NULL, 0, &xOtherLen, ( const unsigned char * ) pucS1, pucS2 - pucS1 ); if( lRet == MBEDTLS_ERR_BASE64_INVALID_CHARACTER ) { return( lRet ); } if( xOtherLen > *pxOlen ) { return( -1 ); } if( ( lRet = mbedtls_base64_decode( pucOutput, xOtherLen, &xOtherLen, ( const unsigned char * ) pucS1, pucS2 - pucS1 ) ) != 0 ) { return( lRet ); } *pxOlen = xOtherLen; return( 0 ); } /*-----------------------------------------------------------*/ /* This function is a modified version of the static function * rsa_rsassa_pkcs1_v15_encode() inside of rsa.c in mbedTLS. It has been extracted * so that corePKCS11 libraries and testing may use it. */ /* Construct a PKCS v1.5 encoding of a hashed message * * This is used both for signature generation and verification. * * Parameters: * - md_alg: Identifies the hash algorithm used to generate the given hash; * MBEDTLS_MD_NONE if raw data is signed. * - hashlen: Length of hash in case hashlen is MBEDTLS_MD_NONE. * - hash: Buffer containing the hashed message or the raw data. * - dst_len: Length of the encoded message. * - dst: Buffer to hold the encoded message. * * Assumptions: * - hash has size hashlen if md_alg == MBEDTLS_MD_NONE. * - hash has size corresponding to md_alg if md_alg != MBEDTLS_MD_NONE. * - dst points to a buffer of size at least dst_len. * */ /* \brief Formats cryptographically hashed data for RSA signing in accordance * with PKCS #1 version 1.5. * * Currently assumes SHA-256. */ int PKI_RSA_RSASSA_PKCS1_v15_Encode( const unsigned char * hash, size_t dst_len, unsigned char * dst ) { size_t oid_size = 0; size_t nb_pad = dst_len; unsigned char * p = dst; const char * oid = NULL; mbedtls_md_type_t md_alg = MBEDTLS_MD_SHA256; unsigned int hashlen = 0; const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type( md_alg ); if( md_info == NULL ) { return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); } if( mbedtls_oid_get_oid_by_md( md_alg, &oid, &oid_size ) != 0 ) { return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); } hashlen = mbedtls_md_get_size( md_info ); /* Double-check that 8 + hashlen + oid_size can be used as a * 1-byte ASN.1 length encoding and that there's no overflow. */ if( ( 8 + hashlen + oid_size >= 0x80 ) || ( 10 + hashlen < hashlen ) || ( 10 + hashlen + oid_size < 10 + hashlen ) ) { return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); } /* * Static bounds check: * - Need 10 bytes for five tag-length pairs. * (Insist on 1-byte length encodings to protect against variants of * Bleichenbacher's forgery attack against lax PKCS#1v1.5 verification) * - Need hashlen bytes for hash * - Need oid_size bytes for hash alg OID. */ if( nb_pad < 10 + hashlen + oid_size ) { return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); } nb_pad -= 10 + hashlen + oid_size; /* Need space for signature header and padding delimiter (3 bytes), * and 8 bytes for the minimal padding */ if( nb_pad < 3 + 8 ) { return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); } nb_pad -= 3; /* Now nb_pad is the amount of memory to be filled * with padding, and at least 8 bytes long. */ /* Write signature header and padding */ *p++ = 0; *p++ = MBEDTLS_RSA_SIGN; memset( p, 0xFF, nb_pad ); p += nb_pad; *p++ = 0; /* Are we signing raw data? */ if( md_alg == MBEDTLS_MD_NONE ) { memcpy( p, hash, hashlen ); return( 0 ); } /* Signing hashed data, add corresponding ASN.1 structure * * DigestInfo ::= SEQUENCE { * digestAlgorithm DigestAlgorithmIdentifier, * digest Digest } * DigestAlgorithmIdentifier ::= AlgorithmIdentifier * Digest ::= OCTET STRING * * Schematic: * TAG-SEQ + LEN [ TAG-SEQ + LEN [ TAG-OID + LEN [ OID ] * TAG-NULL + LEN [ NULL ] ] * TAG-OCTET + LEN [ HASH ] ] */ *p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED; *p++ = ( unsigned char ) ( 0x08 + oid_size + hashlen ); *p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED; *p++ = ( unsigned char ) ( 0x04 + oid_size ); *p++ = MBEDTLS_ASN1_OID; *p++ = ( unsigned char ) oid_size; memcpy( p, oid, oid_size ); p += oid_size; *p++ = MBEDTLS_ASN1_NULL; *p++ = 0x00; *p++ = MBEDTLS_ASN1_OCTET_STRING; *p++ = ( unsigned char ) hashlen; memcpy( p, hash, hashlen ); p += hashlen; /* Just a sanity-check, should be automatic * after the initial bounds check. */ if( p != dst + dst_len ) { mbedtls_platform_zeroize( dst, dst_len ); return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); } return( 0 ); }