blob: aa8a2ea03fd0addf758853a4429ab1604df0576d [file] [log] [blame]
/**
* @file configCmdSet.c
* @author NXP Semiconductors
* @version 1.0
* @par License
*
* Copyright 2017 NXP
* SPDX-License-Identifier: Apache-2.0
*
* @par Description
* Command handling for 'set'. Includes optional console handling
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
// project specific include files
#include "sm_types.h"
#include "sm_apdu.h"
#include "tst_sm_util.h"
#include "tst_a71ch_util.h"
#include "probeAxUtil.h"
#include "configCmd.h"
#include "configCli.h"
#include "a71_debug.h"
#include "axHostCrypto.h"
#include "tstHostCrypto.h"
#ifdef OPENSSL
#include <openssl/pem.h>
#endif
#define FLOW_VERBOSE_PROBE_A70
#ifdef FLOW_VERBOSE_PROBE_A70
#define FPRINTF(...) printf (__VA_ARGS__)
#else
#define FPRINTF(...)
#endif
// Warning: defining DBG_PROBE_A70 also exposes Private Key being set in log
// #define DBG_PROBE_A70
#ifdef DBG_PROBE_A70
#define DBGPRINTF(...) printf (__VA_ARGS__)
#else
#define DBGPRINTF(...)
#endif
#ifdef OPENSSL
/*
The following macro defines a function body with the following
function signature:
EC_KEY *PEM_read_eckey(FILE *fp, EC_KEY **x, pem_password_cb *cb, void *u)
*/
IMPLEMENT_PEM_read_fp(eckey, EC_KEY, "EC PRIVATE KEY", ECPrivateKey)
#endif
/**
* Wrap key
*/
int a7xConfigCmdkWrapping(U8 * key, U16 * keyLen, U8 * wrapKey, U16 wrapKeyLen) {
U8 wrappedKey[72];
U16 wrappedKeyLen = sizeof(wrappedKey);
U16 ret = 0;
if (*keyLen == 65) { // public key
ret = HOSTCRYPTO_AesWrapKeyRFC3394(wrapKey, wrapKeyLen, wrappedKey, &wrappedKeyLen,
key + 1, (*keyLen) - 1);
}
else {
ret = HOSTCRYPTO_AesWrapKeyRFC3394(wrapKey, wrapKeyLen, wrappedKey, &wrappedKeyLen,
key, *keyLen);
}
if (ret == SW_OK) {
*keyLen = wrappedKeyLen;
memcpy(key, wrappedKey, wrappedKeyLen);
}
else {
return AX_CLI_WRAP_ERROR;
}
return AX_CLI_EXEC_OK;
}
/**
* A hook for the command line handler to invoke A71 commands
*/
int a7xConfigCmdSetGp(U16 offset, U8 *gpData, U16 gpDataLen, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
*sw = a7xCmdSetGp(offset, gpData, gpDataLen);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
return error;
}
/**
* API wrapper for set gp command. Can be called from GUI.
*/
U16 a7xCmdSetGp(U16 offset, U8 *gpData, U16 gpDataLen)
{
U16 sw;
sw = A71_SetGpData(offset, gpData, gpDataLen);
return sw;
}
/**
* A hook for the command line handler to invoke A71 commands
*/
// #define DUMP_DER_IN_FILE
int a7xConfigCmdSetGpFromPemfile(U16 offset, char *szFilename, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
X509 *x;
FILE *fp;
#ifdef DUMP_DER_IN_FILE
FILE *fpOut;
#endif
int len;
unsigned char *buf;
printf("Filename: %s\n", szFilename);
fp = fopen(szFilename, "r");
if (!fp)
{
printf("Unable to open the file: %s\n", szFilename);
return AX_CLI_FILE_OPEN_FAILED;
}
x = PEM_read_X509(fp, NULL, NULL, NULL);
if (x == NULL)
{
printf("%s is not a valid pem file / certificate\n", szFilename);
fclose(fp);
return AX_CLI_FILE_PEM_READ_FAILED;
}
#ifdef DUMP_DER_IN_FILE
fpOut = fopen("cert.der", "wb");
if (!fpOut)
{
printf("Unable to open the file: cert.der\n");
return AX_CLI_FILE_OPEN_FAILED;
}
i2d_X509_fp(fpOut, x);
fclose(fpOut);
#endif
buf = NULL;
len = i2d_X509(x, &buf);
if (len < 0)
{
fclose(fp);
return AX_CLI_PEM_CONVERT_FAILED;
}
else
{
int nStorageSize = A7X_CONFIG_GP_STORAGE_MAX; // No check is done on the actual GP storage size of the device attached
int nMaxPos = offset + len;
printf("Certificate Size (DER format) = %d byte\n", len);
// Write to GP storage
// (1) Is there enough storage?
if (nMaxPos > nStorageSize)
{
printf("nMaxPos=%d; nStorageSize=%d\n", nMaxPos, nStorageSize);
// No point in writing certificate as it will not fit
free(buf);
fclose(fp);
return AX_CLI_BUFFER_SIZE_ERROR;
}
// (2) Write (and hope no segment is locked along the way)
*sw = A71_SetGpData(offset, buf, (U16)len);
free(buf);
fclose(fp);
}
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
else
{
error = AX_CLI_EXEC_FAILED;
}
return error;
}
/**
* A hook for the command line handler to invoke A71 commands
*/
int a7xConfigCmdSetEccWrap(a71_SecureStorageClass_t ssc, U8 index, eccKeyComponents_t *eccKc, U8 * wrapKey, U16 wrapKeyLen, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
switch (ssc)
{
case A71_SSC_KEY_PAIR:
error = a7xConfigCmdkWrapping(eccKc->priv, &eccKc->privLen, wrapKey, wrapKeyLen);
break;
case A71_SSC_PUBLIC_KEY:
error = a7xConfigCmdkWrapping(eccKc->pub, &eccKc->pubLen, wrapKey, wrapKeyLen);
if (error != AX_CLI_EXEC_OK) { return error; }
break;
default:
break;
}
*sw = a7xCmdSetEcc(ssc, index, eccKc);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
return error;
}
/**
* A hook for the command line handler to invoke A71 commands
*/
int a7xConfigCmdSetEcc(a71_SecureStorageClass_t ssc, U8 index, eccKeyComponents_t *eccKc, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
*sw = a7xCmdSetEcc(ssc, index, eccKc);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
return error;
}
/**
* API wrapper for set ecc (keypair/pub) command. Can be called from GUI.
*/
U16 a7xCmdSetEcc(a71_SecureStorageClass_t ssc, U8 index, eccKeyComponents_t *eccKc)
{
U16 sw;
switch (ssc)
{
case A71_SSC_KEY_PAIR:
sw = A71_SetEccKeyPair(index, eccKc->pub, eccKc->pubLen, eccKc->priv, eccKc->privLen);
break;
case A71_SSC_PUBLIC_KEY:
sw = A71_SetEccPublicKey(index, eccKc->pub, eccKc->pubLen);
break;
default:
sw = A7X_CONFIG_STATUS_API_ERROR;
break;
}
return sw;
}
/**
* A hook for the command line handler to invoke A71 commands
*/
int a7xConfigCmdSetEccFromPemfile(a71_SecureStorageClass_t ssc, U8 index, char *szFilename, int argc, char ** argv, int *argCurrent, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
eccKeyComponents_t eccKc;
eccKc.bits = 256;
eccKc.curve = ECCCurve_NIST_P256;
eccKc.privLen = 0;
eccKc.pubLen = 0;
error = a7xConfigGetEccKcFromPemfile(&eccKc, ssc, szFilename);
if (error == AX_CLI_EXEC_OK)
{
if (ssc == A71_SSC_KEY_PAIR) {
error = a7xConfigCmdCheckWrapping(eccKc.priv, &eccKc.privLen, argc, argv, argCurrent);
}
else if (ssc == A71_SSC_PUBLIC_KEY) {
error = a7xConfigCmdCheckWrapping(eccKc.pub, &eccKc.pubLen, argc, argv, argCurrent);
}
if (error != AX_CLI_EXEC_OK) { return AX_CLI_EXEC_OK; }
error = AX_CLI_EXEC_FAILED;
*sw = a7xCmdSetEcc(ssc, index, &eccKc);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
}
return error;
}
/**
* Get ecc (keypair/pub) as key pair components (key value contained in PEM key). Can be called from GUI.
*/
int a7xConfigGetEccKcFromPemfile(eccKeyComponents_t *eccKc, a71_SecureStorageClass_t ssc, const char *szKeyFile)
{
#ifdef OPENSSL
// eccKeyComponents_t eccKcTls;
FILE *fp;
EC_KEY *ec_key = NULL;
EVP_PKEY * pkey = NULL;
const EC_POINT *pub_key = NULL;
const BIGNUM *key_bn = BN_new();
U16 bufLen = 0;
/* Read external key file and set public and (optional) private key values */
fp = fopen(szKeyFile, "r");
if (!fp)
{
printf("Unable to open the file: %s\n", szKeyFile);
return AX_CLI_FILE_OPEN_FAILED;
}
ec_key = PEM_read_eckey(fp, NULL, NULL, NULL);
// TODO: Evaluate/Investigate whether comparing to NULL captures all error conditions
if (ec_key == NULL)
{
// Need to set the file to start and try to read t for public key
fseek(fp, 0, SEEK_SET);
// If pem file contains only public key read it
pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
if (pkey != NULL) {
ec_key = EVP_PKEY_get1_EC_KEY(pkey);
}
if (ec_key == NULL)
{
printf("Failed to extract ECC key from %s.\n", szKeyFile);
fclose(fp);
return AX_CLI_FILE_PEM_READ_FAILED;
}
}
fclose(fp);
// TODO: Compare the ECC keypair's curve - as stored in the PEM file - with the one claimed in cryptoType/bits
if (ssc == A71_SSC_KEY_PAIR)
{
U16 sw;
sw = HOSTCRYPTO_GetPrivateKey(ec_key, eccKc->priv, &(eccKc->privLen), (U16)sizeof(eccKc->priv));
if (sw != SW_OK)
{
printf("HOSTCRYPTO_GetPrivateKey failed with 0x%04X.\n", sw);
return AX_CLI_PEM_CONVERT_FAILED;
}
if ( ((eccKc->privLen)*8) != eccKc->bits )
{
printf("KeyLen in pemfile (%d) does not match bits (%d) in function invocation.\n", bufLen*8, eccKc->bits);
return AX_CLI_BIT_CURVE_ERROR;
}
#ifdef DBG_PROBE_A70
axPrintByteArray("ECCPrivateKey", eccKc->priv, eccKc->privLen, AX_COLON_32);
#else
printf("ECCPrivateKey: hidden\n");
#endif
}
// How do we know the key length?
// Set Public Key
pub_key = EC_KEY_get0_public_key(ec_key);
key_bn = EC_POINT_point2bn(EC_KEY_get0_group(ec_key), pub_key, POINT_CONVERSION_UNCOMPRESSED, NULL, NULL);
eccKc->pubLen = (U16)BN_bn2bin(key_bn, eccKc->pub);
bufLen = (U16)(EC_GROUP_get_degree(EC_KEY_get0_group(ec_key))+7)/8;
if ( (bufLen*8) != eccKc->bits )
{
printf("KeyLen in pemfile (%d) does not match bits (%d) in function invocation.\n", bufLen*8, eccKc->bits);
return AX_CLI_BIT_CURVE_ERROR;
}
// bufLen = 2*bufLen + 1; //Public key length
axPrintByteArray("ECCPublicKey", eccKc->pub, eccKc->pubLen, AX_COLON_32);
return AX_CLI_EXEC_OK;
#else
return AX_CLI_NOT_IMPLEMENTED;
#endif
}
/**
* A hook for the command line handler to invoke A71 commands
* Applied Keywrapping is implicit in the length of the secret
* 16 byte: no key wrapping
* 24 byte: wrapped with rfc3394
*/
int a7xConfigCmdSetSymWrap(U8 index, U8 *symSecret, U16 symSecretLen, U8 * wrapKey, U16 wrapKeyLen, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
if (wrapKeyLen>0) {
U16 keyLen = symSecretLen;
error = a7xConfigCmdkWrapping(symSecret, &keyLen, wrapKey, wrapKeyLen);
if (error != AX_CLI_EXEC_OK) { return error; }
*sw = a7xCmdSetSym(index, symSecret, keyLen);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
}
else {
*sw = a7xCmdSetSym(index, symSecret, symSecretLen);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
}
return error;
}
/**
* A hook for the command line handler to invoke A71 commands
* Applied Keywrapping is implicit in the length of the secret
* 16 byte: no key wrapping
* 24 byte: wrapped with rfc3394
*/
int a7xConfigCmdSetSym(U8 index, U8 *symSecret, U16 symSecretLen, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
*sw = a7xCmdSetSym(index, symSecret, symSecretLen);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
return error;
}
/**
* API wrapper for set sym command. Can be called from GUI.
* Applied Keywrapping is implicit in the length of the secret
*
*/
U16 a7xCmdSetSymWrap(U8 index, U8 *symSecret, U16 symSecretLen, U8 * wrapKey, U16 wrapKeyLen) {
U16 error = AX_CLI_EXEC_FAILED;
if (wrapKeyLen>0) {
U16 keyLen = symSecretLen;
error = (U16)a7xConfigCmdkWrapping(symSecret, &keyLen, wrapKey, wrapKeyLen);
if (error != AX_CLI_EXEC_OK) { return error; }
return a7xCmdSetSym(index, symSecret, keyLen);
}
else {
return a7xCmdSetSym(index, symSecret, symSecretLen);
}
}
/**
* API wrapper for set sym command. Can be called from GUI.
* Applied Keywrapping is implicit in the length of the secret
* 16 byte: no key wrapping
* 24 byte: wrapped with rfc3394
*/
U16 a7xCmdSetSym(U8 index, U8 *symSecret, U16 symSecretLen)
{
U16 sw;
switch (symSecretLen)
{
case 16:
sw = A71_SetSymKey(index, symSecret, symSecretLen);
break;
case 24:
sw = A71_SetRfc3394WrappedAesKey(index, symSecret, symSecretLen);
break;
default:
sw = A7X_CONFIG_STATUS_API_ERROR;
break;
}
return sw;
}
/**
* A hook for the command line handler to invoke A71 commands
*/
int a7xConfigCmdSetCnt(U8 index, U8 *cnt, U16 cntLen, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
*sw = a7xCmdSetCnt(index, cnt, cntLen);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
return error;
}
/**
* API wrapper for set monotonic counter command. Can be called from GUI.
*/
U16 a7xCmdSetCnt(U8 index, U8 *cnt, U16 cntLen)
{
U16 sw;
if (cntLen != 4)
{
DBGPRINTF("axCmdSetCnt(..., cntLen=%d)\n", cntLen);
sw = A7X_CONFIG_STATUS_API_ERROR;
}
else
{
U32 counter;
counter = (cnt[0] << 24) + (cnt[1] << 16) + (cnt[2] << 8) + cnt[3];
sw = A71_SetCounter(index, counter);
}
return sw;
}
/**
* A hook for the command line handler to invoke A71 commands
* Applied Keywrapping is implicit in the length of the config key
* 16 byte: no key wrapping
* 24 byte: wrapped with rfc3394
*/
int a7xConfigCmdSetConfigKey(U8 index, U8 *configKey, U16 configKeyLen, U16 *sw)
{
int error = AX_CLI_EXEC_FAILED;
*sw = a7xCmdSetConfigKey(index, configKey, configKeyLen);
if (*sw == SW_OK)
{
error = AX_CLI_EXEC_OK;
}
return error;
}
/**
* API wrapper for SetConfigKey command. Can be called from GUI.
* Applied Keywrapping is implicit in the length of the secret
* 16 byte: no key wrapping
* 24 byte: wrapped with rfc3394
*/
U16 a7xCmdSetConfigKey(U8 index, U8 *configKey, U16 configKeyLen)
{
U16 sw;
switch (configKeyLen)
{
case 16:
sw = A71_SetConfigKey(index, configKey, configKeyLen);
break;
case 24:
sw = A71_SetRfc3394WrappedConfigKey(index, configKey, configKeyLen);
break;
default:
sw = A7X_CONFIG_STATUS_API_ERROR;
break;
}
return sw;
}