mirror of
https://github.com/polhenarejos/pico-keys-sdk
synced 2026-05-28 17:11:23 +02:00
Derive secp256k1 from SE.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
@@ -24,53 +24,98 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "mbedtls/bignum.h"
|
||||||
|
#include "mbedtls/ecp.h"
|
||||||
#include "mbedtls/sha256.h"
|
#include "mbedtls/sha256.h"
|
||||||
|
|
||||||
#define CF_SAFE_RELEASE(x) do { if ((x) != NULL) CFRelease(x); } while (0)
|
#define CF_SAFE_RELEASE(x) do { if ((x) != NULL) CFRelease(x); } while (0)
|
||||||
#define SE_KEY_TAG "com.picokeys.novus.se.key"
|
#define SE_KEY_TAG "com.picokeys.novus.se.key"
|
||||||
|
#define LOCAL_KEY_TAG "com.picokeys.novus.local.ecdh.key"
|
||||||
|
|
||||||
static int sec_err(const char *ctx, OSStatus st) {
|
static int sec_err(const char *ctx, OSStatus st) {
|
||||||
fprintf(stderr, "[macos-se] %s failed: OSStatus=%d\n", ctx, (int)st);
|
fprintf(stderr, "[macos-se] %s failed: OSStatus=%d\n", ctx, (int)st);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int macos_se_vault_load_or_create_key2(uint8_t out_key32[32]) {
|
static int derive_secp256k1_privkey_from_secret(const uint8_t *secret, size_t secret_len, uint8_t out_key32[32]) {
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
|
uint8_t digest[32];
|
||||||
|
const uint8_t label[] = "pico-novus/se-ecdh-to-k1-v1";
|
||||||
|
mbedtls_ecp_group grp;
|
||||||
|
mbedtls_mpi x, n_minus_1;
|
||||||
|
|
||||||
|
if (!secret || secret_len == 0 || !out_key32) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_sha256_context sha;
|
||||||
|
mbedtls_sha256_init(&sha);
|
||||||
|
mbedtls_sha256_starts(&sha, 0);
|
||||||
|
mbedtls_sha256_update(&sha, label, sizeof(label) - 1);
|
||||||
|
mbedtls_sha256_update(&sha, secret, secret_len);
|
||||||
|
mbedtls_sha256_finish(&sha, digest);
|
||||||
|
mbedtls_sha256_free(&sha);
|
||||||
|
|
||||||
|
mbedtls_ecp_group_init(&grp);
|
||||||
|
mbedtls_mpi_init(&x);
|
||||||
|
mbedtls_mpi_init(&n_minus_1);
|
||||||
|
|
||||||
|
if (mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256K1) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (mbedtls_mpi_read_binary(&x, digest, sizeof(digest)) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (mbedtls_mpi_copy(&n_minus_1, &grp.N) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (mbedtls_mpi_sub_int(&n_minus_1, &n_minus_1, 1) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (mbedtls_mpi_mod_mpi(&x, &x, &n_minus_1) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (mbedtls_mpi_add_int(&x, &x, 1) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (mbedtls_mpi_write_binary(&x, out_key32, 32) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
mbedtls_mpi_free(&n_minus_1);
|
||||||
|
mbedtls_mpi_free(&x);
|
||||||
|
mbedtls_ecp_group_free(&grp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int load_or_create_p256_key(const char *tag_str, bool in_secure_enclave, SecKeyRef *out_private_key) {
|
||||||
SecKeyRef private_key = NULL;
|
SecKeyRef private_key = NULL;
|
||||||
SecKeyRef public_key = NULL;
|
|
||||||
CFDataRef tag = NULL;
|
CFDataRef tag = NULL;
|
||||||
CFDictionaryRef find_query = NULL;
|
CFDictionaryRef find_query = NULL;
|
||||||
CFDictionaryRef priv_attrs = NULL;
|
CFDictionaryRef priv_attrs = NULL;
|
||||||
CFNumberRef key_size_num = NULL;
|
CFNumberRef key_size_num = NULL;
|
||||||
CFDictionaryRef attrs = NULL;
|
CFDictionaryRef attrs = NULL;
|
||||||
CFDataRef pub_data = NULL;
|
|
||||||
CFErrorRef err = NULL;
|
CFErrorRef err = NULL;
|
||||||
OSStatus st;
|
OSStatus st;
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
tag = CFDataCreate(NULL, (const UInt8 *)SE_KEY_TAG, (CFIndex)strlen(SE_KEY_TAG));
|
if (!tag_str || !out_private_key) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_private_key = NULL;
|
||||||
|
tag = CFDataCreate(NULL, (const UInt8 *)tag_str, (CFIndex)strlen(tag_str));
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
fprintf(stderr, "[macos-se] CFDataCreate(tag) failed\n");
|
fprintf(stderr, "[macos-se] CFDataCreate(tag) failed\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *find_keys[] = {
|
const void *find_keys[] = { kSecClass, kSecAttrApplicationTag, kSecAttrKeyType, kSecReturnRef };
|
||||||
kSecClass,
|
const void *find_vals[] = { kSecClassKey, tag, kSecAttrKeyTypeECSECPrimeRandom, kCFBooleanTrue };
|
||||||
kSecAttrApplicationTag,
|
find_query = CFDictionaryCreate(NULL, find_keys, find_vals, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
kSecAttrKeyType,
|
|
||||||
kSecReturnRef
|
|
||||||
};
|
|
||||||
const void *find_vals[] = {
|
|
||||||
kSecClassKey,
|
|
||||||
tag,
|
|
||||||
kSecAttrKeyTypeECSECPrimeRandom,
|
|
||||||
kCFBooleanTrue
|
|
||||||
};
|
|
||||||
find_query = CFDictionaryCreate(NULL,
|
|
||||||
find_keys,
|
|
||||||
find_vals,
|
|
||||||
4,
|
|
||||||
&kCFTypeDictionaryKeyCallBacks,
|
|
||||||
&kCFTypeDictionaryValueCallBacks);
|
|
||||||
if (!find_query) {
|
if (!find_query) {
|
||||||
fprintf(stderr, "[macos-se] CFDictionaryCreate(find_query) failed\n");
|
fprintf(stderr, "[macos-se] CFDictionaryCreate(find_query) failed\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -80,12 +125,7 @@ static int macos_se_vault_load_or_create_key2(uint8_t out_key32[32]) {
|
|||||||
if (st == errSecItemNotFound) {
|
if (st == errSecItemNotFound) {
|
||||||
const void *priv_keys[] = { kSecAttrIsPermanent, kSecAttrApplicationTag };
|
const void *priv_keys[] = { kSecAttrIsPermanent, kSecAttrApplicationTag };
|
||||||
const void *priv_vals[] = { kCFBooleanTrue, tag };
|
const void *priv_vals[] = { kCFBooleanTrue, tag };
|
||||||
priv_attrs = CFDictionaryCreate(NULL,
|
priv_attrs = CFDictionaryCreate(NULL, priv_keys, priv_vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
priv_keys,
|
|
||||||
priv_vals,
|
|
||||||
2,
|
|
||||||
&kCFTypeDictionaryKeyCallBacks,
|
|
||||||
&kCFTypeDictionaryValueCallBacks);
|
|
||||||
if (!priv_attrs) {
|
if (!priv_attrs) {
|
||||||
fprintf(stderr, "[macos-se] CFDictionaryCreate(priv_attrs) failed\n");
|
fprintf(stderr, "[macos-se] CFDictionaryCreate(priv_attrs) failed\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -98,24 +138,15 @@ static int macos_se_vault_load_or_create_key2(uint8_t out_key32[32]) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *keys[] = {
|
if (in_secure_enclave) {
|
||||||
kSecAttrKeyType,
|
const void *keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits, kSecAttrTokenID, kSecPrivateKeyAttrs };
|
||||||
kSecAttrKeySizeInBits,
|
const void *vals[] = { kSecAttrKeyTypeECSECPrimeRandom, key_size_num, kSecAttrTokenIDSecureEnclave, priv_attrs };
|
||||||
kSecAttrTokenID,
|
attrs = CFDictionaryCreate(NULL, keys, vals, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
kSecPrivateKeyAttrs
|
} else {
|
||||||
};
|
const void *keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits, kSecPrivateKeyAttrs };
|
||||||
const void *vals[] = {
|
const void *vals[] = { kSecAttrKeyTypeECSECPrimeRandom, key_size_num, priv_attrs };
|
||||||
kSecAttrKeyTypeECSECPrimeRandom,
|
attrs = CFDictionaryCreate(NULL, keys, vals, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
key_size_num,
|
}
|
||||||
kSecAttrTokenIDSecureEnclave,
|
|
||||||
priv_attrs
|
|
||||||
};
|
|
||||||
attrs = CFDictionaryCreate(NULL,
|
|
||||||
keys,
|
|
||||||
vals,
|
|
||||||
4,
|
|
||||||
&kCFTypeDictionaryKeyCallBacks,
|
|
||||||
&kCFTypeDictionaryValueCallBacks);
|
|
||||||
if (!attrs) {
|
if (!attrs) {
|
||||||
fprintf(stderr, "[macos-se] CFDictionaryCreate(attrs) failed\n");
|
fprintf(stderr, "[macos-se] CFDictionaryCreate(attrs) failed\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -129,36 +160,71 @@ static int macos_se_vault_load_or_create_key2(uint8_t out_key32[32]) {
|
|||||||
}
|
}
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
printf("[macos-se] Created Secure Enclave key '%s'\n", SE_KEY_TAG);
|
printf("[macos-se] Created key '%s'%s\n", tag_str, in_secure_enclave ? " in Secure Enclave" : "");
|
||||||
} else if (st != errSecSuccess) {
|
} else if (st != errSecSuccess) {
|
||||||
sec_err("SecItemCopyMatching", st);
|
sec_err("SecItemCopyMatching", st);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
} else {
|
} else {
|
||||||
printf("[macos-se] Using existing Secure Enclave key '%s'\n", SE_KEY_TAG);
|
printf("[macos-se] Using existing key '%s'\n", tag_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
public_key = SecKeyCopyPublicKey(private_key);
|
*out_private_key = private_key;
|
||||||
if (!public_key) {
|
private_key = NULL;
|
||||||
fprintf(stderr, "[macos-se] SecKeyCopyPublicKey failed\n");
|
rc = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (err) {
|
||||||
|
CFRelease(err);
|
||||||
|
}
|
||||||
|
CF_SAFE_RELEASE(attrs);
|
||||||
|
CF_SAFE_RELEASE(key_size_num);
|
||||||
|
CF_SAFE_RELEASE(priv_attrs);
|
||||||
|
CF_SAFE_RELEASE(find_query);
|
||||||
|
CF_SAFE_RELEASE(tag);
|
||||||
|
CF_SAFE_RELEASE(private_key);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int macos_se_vault_load_or_create_key2(uint8_t out_key32[32]) {
|
||||||
|
int rc = -1;
|
||||||
|
SecKeyRef se_private_key = NULL;
|
||||||
|
SecKeyRef local_private_key = NULL;
|
||||||
|
SecKeyRef local_public_key = NULL;
|
||||||
|
CFDataRef shared_secret = NULL;
|
||||||
|
CFDictionaryRef ecdh_params = NULL;
|
||||||
|
CFErrorRef err = NULL;
|
||||||
|
|
||||||
|
if (load_or_create_p256_key(SE_KEY_TAG, true, &se_private_key) != 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (load_or_create_p256_key(LOCAL_KEY_TAG, false, &local_private_key) != 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub_data = SecKeyCopyExternalRepresentation(public_key, &err);
|
local_public_key = SecKeyCopyPublicKey(local_private_key);
|
||||||
if (!pub_data) {
|
if (!local_public_key) {
|
||||||
fprintf(stderr, "[macos-se] SecKeyCopyExternalRepresentation failed\n");
|
fprintf(stderr, "[macos-se] SecKeyCopyPublicKey(local_private_key) failed\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ecdh_params = CFDictionaryCreate(NULL, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
|
if (!ecdh_params) {
|
||||||
|
fprintf(stderr, "[macos-se] CFDictionaryCreate(ecdh_params) failed\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_secret = SecKeyCopyKeyExchangeResult(se_private_key, kSecKeyAlgorithmECDHKeyExchangeStandard, local_public_key, ecdh_params, &err);
|
||||||
|
if (!shared_secret) {
|
||||||
|
fprintf(stderr, "[macos-se] SecKeyCopyKeyExchangeResult failed\n");
|
||||||
if (err) {
|
if (err) {
|
||||||
CFShow(err);
|
CFShow(err);
|
||||||
}
|
}
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
mbedtls_sha256(CFDataGetBytePtr(pub_data),
|
if (derive_secp256k1_privkey_from_secret(CFDataGetBytePtr(shared_secret), (size_t)CFDataGetLength(shared_secret), out_key32) != 0) {
|
||||||
(size_t)CFDataGetLength(pub_data),
|
fprintf(stderr, "[macos-se] failed deriving secp256k1 private key from ECDH secret\n");
|
||||||
out_key32,
|
goto cleanup;
|
||||||
0);
|
|
||||||
if (!memcmp(out_key32, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
|
||||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32)) {
|
|
||||||
out_key32[31] = 1;
|
|
||||||
}
|
}
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
||||||
@@ -166,14 +232,11 @@ cleanup:
|
|||||||
if (err) {
|
if (err) {
|
||||||
CFRelease(err);
|
CFRelease(err);
|
||||||
}
|
}
|
||||||
CF_SAFE_RELEASE(pub_data);
|
CF_SAFE_RELEASE(ecdh_params);
|
||||||
CF_SAFE_RELEASE(public_key);
|
CF_SAFE_RELEASE(shared_secret);
|
||||||
CF_SAFE_RELEASE(attrs);
|
CF_SAFE_RELEASE(local_public_key);
|
||||||
CF_SAFE_RELEASE(key_size_num);
|
CF_SAFE_RELEASE(local_private_key);
|
||||||
CF_SAFE_RELEASE(priv_attrs);
|
CF_SAFE_RELEASE(se_private_key);
|
||||||
CF_SAFE_RELEASE(find_query);
|
|
||||||
CF_SAFE_RELEASE(tag);
|
|
||||||
CF_SAFE_RELEASE(private_key);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -204,6 +267,7 @@ void otp_platform_init(const uint8_t **otp_key_1_out, const uint8_t **otp_key_2_
|
|||||||
if (macos_se_vault_load_or_create_key2(_otp2) != 0) {
|
if (macos_se_vault_load_or_create_key2(_otp2) != 0) {
|
||||||
printf("Warning: failed to load MACOS_APP Secure Enclave key; using dummy otp_key_2\n");
|
printf("Warning: failed to load MACOS_APP Secure Enclave key; using dummy otp_key_2\n");
|
||||||
}
|
}
|
||||||
|
DEBUG_DATA(_otp2, 32);
|
||||||
#endif
|
#endif
|
||||||
*otp_key_1_out = _otp1;
|
*otp_key_1_out = _otp1;
|
||||||
*otp_key_2_out = _otp2;
|
*otp_key_2_out = _otp2;
|
||||||
|
|||||||
Reference in New Issue
Block a user