blob: 097b001417dead0db8e8706fc34278c23b0a3043 [file] [log] [blame]
/*
* 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)
*/
/**
* @file mbedtls_utils.c
* @brief Helper functions originating from mbedTLS.
*/
/* Standard includes. */
#include <string.h>
/* 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 FreeRTOS PKCS #11 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 );
}