blob: 5239ecd8de539483bf52a34f6f789bdb495f6a68 [file] [log] [blame]
/*
* The RSA public-key cryptosystem
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* Copyright 2019,2020 NXP, 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)
*/
/*
* The following sources were referenced in the design of this implementation
* of the RSA algorithm:
*
* [1] A method for obtaining digital signatures and public-key cryptosystems
* R Rivest, A Shamir, and L Adleman
* http://people.csail.mit.edu/rivest/pubs.html#RSA78
*
* [2] Handbook of Applied Cryptography - 1997, Chapter 8
* Menezes, van Oorschot and Vanstone
*
* [3] Malware Guard Extension: Using SGX to Conceal Cache Attacks
* Michael Schwarz, Samuel Weiser, Daniel Gruss, Clementine Maurice and
* Stefan Mangard
* https://arxiv.org/abs/1702.08719v2
*
*/
#include <fsl_sss_se05x_apis.h>
#include <nxLog_sss.h>
#include <stdlib.h>
#include <string.h>
#if SSS_HAVE_APPLET_SE05X_IOT && SSSFTR_RSA
#include "se05x_APDU.h"
uint8_t pkcs1_v15_encode(
sss_se05x_asymmetric_t *context, const uint8_t *hash, size_t hashlen, uint8_t *out, size_t *outLen)
{
size_t oid_size = 0;
size_t nb_pad = 0;
unsigned char *p = out;
/* clang-format off */
char oid1[16] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, };
/* clang-format on */
size_t outlength = 0;
uint16_t key_size_bytes = 0;
smStatus_t ret_val = SM_NOT_OK;
/* Constants */
const uint8_t RSA_Sign = 0x01;
const uint8_t ASN1_sequence = 0x10;
const uint8_t ASN1_constructed = 0x20;
const uint8_t ASN1_oid = 0x06;
const uint8_t ASN1_null = 0x05;
const uint8_t ASN1_octat_string = 0x04;
ret_val = Se05x_API_ReadSize(&context->session->s_ctx, context->keyObject->keyId, &key_size_bytes);
if (ret_val != SM_OK) {
return 1;
}
outlength = key_size_bytes;
nb_pad = outlength;
switch (context->algorithm) {
case kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA1:
oid1[0] = 0x2b;
oid1[1] = 0x0e;
oid1[2] = 0x03;
oid1[3] = 0x02;
oid1[4] = 0x1a;
oid_size = 5;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA224:
oid1[8] = 0x04;
oid_size = 9;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA256:
oid1[8] = 0x01;
oid_size = 9;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA384:
oid1[8] = 0x02;
oid_size = 9;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA512:
oid1[8] = 0x03;
oid_size = 9;
break;
default:
return 1;
}
if (outlength < (hashlen + oid_size + 6 /* DigestInfo TLV overhead */)) {
LOG_E("Intended encoded message length too short");
return 1;
}
if (*outLen < outlength) {
LOG_E("Out buffer memory is less ");
return 1;
}
*outLen = outlength;
/* 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)
return 1;
/*
* 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 1;
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 1;
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++ = RSA_Sign;
memset(p, 0xFF, nb_pad);
p += nb_pad;
*p++ = 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++ = ASN1_sequence | ASN1_constructed;
*p++ = (unsigned char)(0x08 + oid_size + hashlen);
*p++ = ASN1_sequence | ASN1_constructed;
*p++ = (unsigned char)(0x04 + oid_size);
*p++ = ASN1_oid;
*p++ = (unsigned char)oid_size;
memcpy(p, oid1, oid_size);
p += oid_size;
*p++ = ASN1_null;
*p++ = 0x00;
*p++ = ASN1_octat_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 != out + outlength) {
memset(out, 0, outlength);
return 1;
}
return 0;
}
uint8_t pkcs1_v15_encode_no_hash(
sss_se05x_asymmetric_t *context, const uint8_t *hash, size_t hashlen, uint8_t *out, size_t *outLen)
{
uint16_t key_size_bytes = 0;
smStatus_t ret_val = SM_NOT_OK;
ret_val = Se05x_API_ReadSize(&context->session->s_ctx, context->keyObject->keyId, &key_size_bytes);
if (ret_val != SM_OK) {
return 1;
}
if (hashlen > (size_t)(key_size_bytes - 11)) {
return 1;
}
if (*outLen < key_size_bytes) {
return 1;
}
memset(out, 0xFF, *outLen);
out[0] = 0x00;
out[1] = 0x01;
out[key_size_bytes - hashlen - 1] = 0x00;
memcpy(&out[key_size_bytes - hashlen], hash, hashlen);
*outLen = key_size_bytes;
return 0;
}
uint8_t sss_mgf_mask_func(uint8_t *dst,
size_t dlen,
uint8_t *src,
size_t slen,
sss_algorithm_t sha_algorithm,
sss_se05x_asymmetric_t *context)
{
uint8_t mask[64]; /* MAX - SHA512*/
uint8_t counter[4];
uint8_t *p;
size_t i, use_len;
uint8_t ret = 1;
sss_status_t status = kStatus_SSS_Fail;
sss_digest_t digest;
size_t digestLen = 512; /* MAX - SHA512*/
size_t hashlength = slen;
memset(mask, 0, 64);
memset(counter, 0, 4);
status = sss_digest_context_init(&digest, (sss_session_t *)context->session, sha_algorithm, kMode_SSS_Digest);
if (status != kStatus_SSS_Success) {
goto exit;
}
/* Generate and apply dbMask */
p = dst;
while (dlen > 0) {
use_len = hashlength;
if (dlen < hashlength)
use_len = dlen;
status = sss_digest_init(&digest);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, src, slen);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, counter, 4);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_finish(&digest, mask, &digestLen);
if (status != kStatus_SSS_Success) {
goto exit;
}
for (i = 0; i < use_len; ++i)
*p++ ^= mask[i];
counter[3]++;
dlen -= use_len;
}
sss_digest_context_free(&digest);
ret = 0;
exit:
return ret;
}
// Note-1: This function does not implement the full EMSA-PSS Encoding Operation operation
// (refer to RFC 8017 Section 9.1 Figure 2), the caller MUST pass 'mHash' (= Hash(M)) as input
// via function argument(s) hash / haslen.
//
// Note-2: Any hash value passed as input that does not match (in byte length)
// the hash requested for the signature (kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHAxxx)
// will be rejected.
//
uint8_t emsa_encode(sss_se05x_asymmetric_t *context, const uint8_t *hash, size_t hashlen, uint8_t *out, size_t *outLen)
{
size_t outlength = 0;
uint8_t *p = out;
uint8_t salt[64] = {
0,
};
uint32_t saltlength = 0;
uint32_t hashlength = 0;
uint32_t offset = 0;
uint8_t ret = 1;
size_t msb;
sss_rng_context_t rng;
sss_digest_t digest;
sss_algorithm_t sha_algorithm = -1;
size_t digestLen = 512; /* MAX - SHA512*/
sss_status_t status = kStatus_SSS_Fail;
uint16_t key_size_bytes = 0;
smStatus_t ret_val = SM_NOT_OK;
ret_val = Se05x_API_ReadSize(&context->session->s_ctx, context->keyObject->keyId, &key_size_bytes);
if (ret_val != SM_OK) {
goto exit;
}
outlength = key_size_bytes;
switch (context->algorithm) {
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA1:
hashlength = 20;
sha_algorithm = kAlgorithm_SSS_SHA1;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA224:
hashlength = 28;
sha_algorithm = kAlgorithm_SSS_SHA224;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA256:
if (key_size_bytes <= 64) { /* RSA Key size = 512 */
LOG_E("SHA256 not supported with this RSA key");
goto exit;
}
hashlength = 32;
sha_algorithm = kAlgorithm_SSS_SHA256;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA384:
if (key_size_bytes <= 64) { /* RSA Key size = 512 */
LOG_E("SHA384 not supported with this RSA key");
goto exit;
}
hashlength = 48;
sha_algorithm = kAlgorithm_SSS_SHA384;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA512:
if (key_size_bytes <= 128) { /* RSA Key size = 1024 and 512 */
LOG_E("SHA512 not supported with this RSA key");
goto exit;
}
hashlength = 64;
sha_algorithm = kAlgorithm_SSS_SHA512;
break;
default:
goto exit;
}
if (hashlength != hashlen) {
ret_val = SM_NOT_OK;
goto exit;
}
saltlength = hashlength;
*outLen = outlength;
/* Generate salt of length saltlength */
status = sss_rng_context_init(&rng, (sss_session_t *)context->session /* session */);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_rng_get_random(&rng, salt, saltlength);
if (status != kStatus_SSS_Success) {
goto exit;
}
msb = (outlength * 8) - 1;
p += outlength - hashlength * 2 - 2;
*p++ = 0x01;
memcpy(p, salt, saltlength);
p += saltlength;
status = sss_digest_context_init(&digest, (sss_session_t *)context->session, sha_algorithm, kMode_SSS_Digest);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_init(&digest);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, p, 8);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, hash, hashlen);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, salt, saltlength);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_finish(&digest, p, &digestLen);
if (status != kStatus_SSS_Success) {
goto exit;
}
sss_digest_context_free(&digest);
if (msb % 8 == 0)
offset = 1;
/* Apply MGF Mask */
if (0 !=
sss_mgf_mask_func(out + offset, outlength - hashlength - 1 - offset, p, hashlength, sha_algorithm, context))
goto exit;
out[0] &= 0xFF >> (outlength * 8 - msb);
p += hashlength;
*p++ = 0xBC;
ret = 0;
exit:
return ret;
}
uint8_t emsa_decode_and_compare(
sss_se05x_asymmetric_t *context, uint8_t *sig, size_t siglen, uint8_t *hash, size_t hashlen)
{
uint8_t *p;
uint8_t *hash_start;
uint8_t result[512];
uint8_t ret = 1;
uint32_t hlen;
uint8_t zeros[8];
uint32_t observed_salt_len, msb;
uint8_t buf[1024];
sss_algorithm_t sha_algorithm = -1;
sss_digest_t digest;
size_t digestLen = 512; /* MAX - SHA512*/
sss_status_t status = kStatus_SSS_Fail;
memcpy(buf, sig, siglen);
switch (context->algorithm) {
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA1:
hlen = 20;
sha_algorithm = kAlgorithm_SSS_SHA1;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA224:
hlen = 28;
sha_algorithm = kAlgorithm_SSS_SHA224;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA256:
hlen = 32;
sha_algorithm = kAlgorithm_SSS_SHA256;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA384:
hlen = 48;
sha_algorithm = kAlgorithm_SSS_SHA384;
break;
case kAlgorithm_SSS_RSASSA_PKCS1_PSS_MGF1_SHA512:
hlen = 64;
sha_algorithm = kAlgorithm_SSS_SHA512;
break;
default:
goto exit;
}
p = buf;
if (buf[siglen - 1] != 0xBC) {
goto exit;
}
memset(zeros, 0, 8);
msb = (hlen * 8) - 1;
if (buf[0] >> (8 - siglen * 8 + msb))
goto exit;
if (siglen < hlen + 2)
goto exit;
hash_start = p + siglen - hlen - 1;
if (0 != sss_mgf_mask_func(p, siglen - hlen - 1, hash_start, hlen, sha_algorithm, context))
goto exit;
buf[0] &= 0xFF >> ((siglen * 8 - msb) % 8);
while (p < hash_start - 1 && *p == 0)
p++;
if (*p++ != 0x01) {
goto exit;
}
observed_salt_len = hash_start - p;
status = sss_digest_context_init(&digest, (sss_session_t *)context->session, sha_algorithm, kMode_SSS_Digest);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_init(&digest);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, zeros, 8);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, hash, hashlen);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_update(&digest, p, observed_salt_len);
if (status != kStatus_SSS_Success) {
goto exit;
}
status = sss_digest_finish(&digest, result, &digestLen);
if (status != kStatus_SSS_Success) {
goto exit;
}
sss_digest_context_free(&digest);
if (memcmp(hash_start, result, hlen) != 0) {
goto exit;
}
ret = 0;
exit:
return ret;
}
#endif