add real pkcs#8 import of Curve25519 private keys

This commit is contained in:
Steffen Jaeckel 2019-06-09 20:51:59 +02:00
parent 8ea0fb1195
commit e3766e16ca
9 changed files with 254 additions and 103 deletions

View file

@ -359,6 +359,9 @@ int ed25519_export( unsigned char *out, unsigned long *outlen,
int ed25519_import(const unsigned char *in, unsigned long inlen, curve25519_key *key);
int ed25519_import_x509(const unsigned char *in, unsigned long inlen, curve25519_key *key);
int ed25519_import_pkcs8(const unsigned char *in, unsigned long inlen,
const void *pwd, unsigned long pwdlen,
curve25519_key *key);
int ed25519_sign(const unsigned char *msg, unsigned long msglen,
unsigned char *sig, unsigned long *siglen,
@ -381,6 +384,9 @@ int x25519_export( unsigned char *out, unsigned long *outlen,
int x25519_import(const unsigned char *in, unsigned long inlen, curve25519_key *key);
int x25519_import_x509(const unsigned char *in, unsigned long inlen, curve25519_key *key);
int x25519_import_pkcs8(const unsigned char *in, unsigned long inlen,
const void *pwd, unsigned long pwdlen,
curve25519_key *key);
int x25519_shared_secret(const curve25519_key *private_key,
const curve25519_key *public_key,

View file

@ -315,6 +315,12 @@ int crypto_sk_to_pk(unsigned char *pk, const unsigned char *sk);
int crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p);
int crypto_scalarmult_base(unsigned char *q,const unsigned char *n);
typedef int (*sk_to_pk)(unsigned char *pk ,const unsigned char *sk);
int ec25519_import_pkcs8(const unsigned char *in, unsigned long inlen,
const void *pwd, unsigned long pwdlen,
enum ltc_oid_id id, sk_to_pk fp,
curve25519_key *key);
#endif /* LTC_CURVE25519 */
#ifdef LTC_DER

View file

@ -0,0 +1,97 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*/
#include "tomcrypt_private.h"
/**
@file ec25519_import_pkcs8.c
Generic import of a Curve/Ed25519 private key in PKCS#8 format, Steffen Jaeckel
*/
#ifdef LTC_CURVE25519
/**
Generic import of a Curve/Ed25519 private key in PKCS#8 format
@param in The DER-encoded PKCS#8-formatted private key
@param inlen The length of the input data
@param passwd The password to decrypt the private key
@param passwdlen Password's length (octets)
@param key [out] Where to import the key to
@return CRYPT_OK if successful, on error all allocated memory is freed automatically
*/
int ec25519_import_pkcs8(const unsigned char *in, unsigned long inlen,
const void *pwd, unsigned long pwdlen,
enum ltc_oid_id id, sk_to_pk fp,
curve25519_key *key)
{
int err = CRYPT_INVALID_ARG;
ltc_asn1_list *l = NULL;
const char *oid;
ltc_asn1_list alg_id[1];
unsigned char private_key[34];
unsigned long version, key_len;
unsigned long tmpoid[16];
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(key != NULL);
LTC_ARGCHK(fp != NULL);
if ((err = pkcs8_decode_flexi(in, inlen, pwd, pwdlen, &l)) == CRYPT_OK) {
LTC_SET_ASN1(alg_id, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid) / sizeof(tmpoid[0]));
key_len = sizeof(private_key);
if ((err = der_decode_sequence_multi(l->data, l->size,
LTC_ASN1_SHORT_INTEGER, 1uL, &version,
LTC_ASN1_SEQUENCE, 1uL, alg_id,
LTC_ASN1_OCTET_STRING, key_len, private_key,
LTC_ASN1_EOL, 0uL, NULL))
!= CRYPT_OK) {
/* If there are attributes added after the private_key it is tagged with version 1 and
* we get an 'input too long' error but the rest is already decoded and can be
* handled the same as for version 0
*/
if ((err == CRYPT_INPUT_TOO_LONG) && (version == 1)) {
version = 0;
} else {
goto out;
}
}
if ((err = pk_get_oid(id, &oid)) != CRYPT_OK) {
goto out;
}
if ((err = pk_oid_cmp_with_asn1(oid, &alg_id[0])) != CRYPT_OK) {
goto out;
}
if (version == 0) {
key_len = sizeof(key->priv);
if ((err = der_decode_octet_string(private_key, sizeof(private_key), key->priv, &key_len)) == CRYPT_OK) {
fp(key->pub, key->priv);
key->type = PK_PRIVATE;
key->algo = id;
}
} else {
err = CRYPT_PK_INVALID_TYPE;
}
}
out:
if (l) der_free_sequence_flexi(l);
#ifdef LTC_CLEAN_STACK
zeromem(private_key, sizeof(private_key));
#endif
return err;
}
#endif
/* ref: $Format:%D$ */
/* git commit: $Format:%H$ */
/* commit time: $Format:%ai$ */

View file

@ -16,7 +16,7 @@
#ifdef LTC_CURVE25519
/**
Import a Ed25519 key from a binary packet
Import an Ed25519 public key
@param in The packet to read
@param inlen The length of the input packet
@param key [out] Where to import the key to
@ -25,72 +25,19 @@
int ed25519_import(const unsigned char *in, unsigned long inlen, curve25519_key *key)
{
int err;
const char* oid;
ltc_asn1_list alg_id[1];
unsigned char private_key[34];
unsigned long version, key_len;
unsigned long tmpoid[16];
unsigned long key_len;
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(key != NULL);
/* There's only one case where the inlen is equal to the pubkey-size
* and that's a raw pubkey, so let's just do a raw import.
*/
if (inlen == sizeof(key->pub)) {
XMEMCPY(key->pub, in, sizeof(key->pub));
key->type = PK_PUBLIC;
key->algo = PKA_ED25519;
return CRYPT_OK;
}
key_len = sizeof(key->pub);
if ((err = x509_decode_subject_public_key_info(in, inlen, PKA_ED25519, key->pub, &key_len, LTC_ASN1_EOL, NULL, 0uL)) == CRYPT_OK) {
key->type = PK_PUBLIC;
key->algo = PKA_ED25519;
return CRYPT_OK;
}
LTC_SET_ASN1(alg_id, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid)/sizeof(tmpoid[0]));
key_len = sizeof(private_key);
err = der_decode_sequence_multi(in, inlen,
LTC_ASN1_SHORT_INTEGER, 1uL, &version,
LTC_ASN1_SEQUENCE, 1uL, alg_id,
LTC_ASN1_OCTET_STRING, key_len, private_key,
LTC_ASN1_EOL, 0uL, NULL);
if (err != CRYPT_OK) {
if ((err == CRYPT_INPUT_TOO_LONG) && (version == 1)) {
version = 0;
} else {
goto out;
}
}
if ((err = pk_get_oid(PKA_ED25519, &oid)) != CRYPT_OK) {
goto out;
}
if ((err = pk_oid_cmp_with_asn1(oid, &alg_id[0])) != CRYPT_OK) {
goto out;
}
if (version == 0) {
key_len = sizeof(key->priv);
if ((err = der_decode_octet_string(private_key, sizeof(private_key), key->priv, &key_len)) == CRYPT_OK) {
crypto_sk_to_pk(key->pub, key->priv);
key->type = PK_PRIVATE;
key->algo = PKA_ED25519;
}
} else {
err = CRYPT_PK_INVALID_TYPE;
return err;
}
out:
#ifdef LTC_CLEAN_STACK
zeromem(private_key, sizeof(private_key));
#endif
return err;
}
#endif

View file

@ -0,0 +1,38 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*/
#include "tomcrypt_private.h"
/**
@file ed25519_import_pkcs8.c
Import a Ed25519 key in PKCS#8 format, Steffen Jaeckel
*/
#ifdef LTC_CURVE25519
/**
Import a Ed25519 private key in PKCS#8 format
@param in The DER-encoded PKCS#8-formatted private key
@param inlen The length of the input data
@param passwd The password to decrypt the private key
@param passwdlen Password's length (octets)
@param key [out] Where to import the key to
@return CRYPT_OK if successful, on error all allocated memory is freed automatically
*/
int ed25519_import_pkcs8(const unsigned char *in, unsigned long inlen,
const void *pwd, unsigned long pwdlen,
curve25519_key *key)
{
return ec25519_import_pkcs8(in, inlen, pwd, pwdlen, PKA_ED25519, crypto_sk_to_pk, key);
}
#endif
/* ref: $Format:%D$ */
/* git commit: $Format:%H$ */
/* commit time: $Format:%ai$ */

View file

@ -25,11 +25,7 @@
int x25519_import(const unsigned char *in, unsigned long inlen, curve25519_key *key)
{
int err;
const char *oid;
ltc_asn1_list alg_id[1];
unsigned char private_key[34];
unsigned long version, key_len;
unsigned long tmpoid[16];
unsigned long key_len;
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(key != NULL);
@ -49,43 +45,9 @@ int x25519_import(const unsigned char *in, unsigned long inlen, curve25519_key *
key->type = PK_PUBLIC;
key->algo = PKA_X25519;
return CRYPT_OK;
}
LTC_SET_ASN1(alg_id, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid)/sizeof(tmpoid[0]));
key_len = sizeof(private_key);
if ((err = der_decode_sequence_multi(in, inlen,
LTC_ASN1_SHORT_INTEGER, 1uL, &version,
LTC_ASN1_SEQUENCE, 1uL, alg_id,
LTC_ASN1_OCTET_STRING, key_len, private_key,
LTC_ASN1_EOL, 0uL, NULL)) != CRYPT_OK) {
goto out;
}
if ((err = pk_get_oid(PKA_X25519, &oid)) != CRYPT_OK) {
goto out;
}
if ((err = pk_oid_cmp_with_asn1(oid, &alg_id[0])) != CRYPT_OK) {
goto out;
}
if (version == 0) {
key_len = sizeof(key->priv);
if ((err = der_decode_octet_string(private_key, sizeof(private_key), key->priv, &key_len)) == CRYPT_OK) {
crypto_scalarmult_base(key->pub, key->priv);
key->type = PK_PRIVATE;
key->algo = PKA_X25519;
}
} else {
err = CRYPT_PK_INVALID_TYPE;
return err;
}
out:
#ifdef LTC_CLEAN_STACK
zeromem(private_key, sizeof(private_key));
#endif
return err;
}
#endif

View file

@ -0,0 +1,38 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*/
#include "tomcrypt_private.h"
/**
@file x25519_import_pkcs8.c
Import a X25519 key in PKCS#8 format, Steffen Jaeckel
*/
#ifdef LTC_CURVE25519
/**
Import a X25519 private key in PKCS#8 format
@param in The DER-encoded PKCS#8-formatted private key
@param inlen The length of the input data
@param passwd The password to decrypt the private key
@param passwdlen Password's length (octets)
@param key [out] Where to import the key to
@return CRYPT_OK if successful, on error all allocated memory is freed automatically
*/
int x25519_import_pkcs8(const unsigned char *in, unsigned long inlen,
const void *pwd, unsigned long pwdlen,
curve25519_key *key)
{
return ec25519_import_pkcs8(in, inlen, pwd, pwdlen, PKA_X25519, crypto_scalarmult_base, key);
}
#endif
/* ref: $Format:%D$ */
/* git commit: $Format:%H$ */
/* commit time: $Format:%ai$ */

View file

@ -43,6 +43,13 @@ static int _rfc_8410_10_test(void)
{ "MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC"
"oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB"
"Z9w7lshQhqowtrbLDFw4rXAxZuE=", -1 },
/* Another self-created testvector.
* `openssl genpkey -algorithm ed25519 -pass stdin -aes128`
*/
{ "MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAiFflnrBOdwjwICCAAw"
"DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEMzFYoqiT6gxwFx2EA55MUYEQFD1"
"ZLxPNhm4YAsMZaxu5qpLjiZbkWsTHxURb6WhSW8GAbNbTwxeOaA02sUhJg8rx44/"
"N9PzN2QGzIQ1Yv/vHqQ=", -1 },
};
unsigned n;
curve25519_key key;
@ -52,10 +59,22 @@ static int _rfc_8410_10_test(void)
for (n = 0; n < sizeof(rfc_8410_10)/sizeof(rfc_8410_10[0]); ++n) {
buflen = sizeof(buf);
DO(base64_decode(rfc_8410_10[n].b64, strlen(rfc_8410_10[n].b64), buf, &buflen));
if (rfc_8410_10[n].type == -2) {
DO(ed25519_import_x509(buf, buflen, &key));
} else {
DO(ed25519_import(buf, buflen, &key));
switch (n) {
case 0:
DO(ed25519_import(buf, buflen, &key));
break;
case 1:
DO(ed25519_import_x509(buf, buflen, &key));
break;
case 2:
case 3:
DO(ed25519_import_pkcs8(buf, buflen, NULL, 0, &key));
break;
case 4:
DO(ed25519_import_pkcs8(buf, buflen, "123456", 6, &key));
break;
default:
return CRYPT_FAIL_TESTVECTOR;
}
zeromem(buf, sizeof(buf));
if (rfc_8410_10[n].type > 0) {

View file

@ -120,7 +120,7 @@ static int _rfc_7748_6_test(void)
static int _rfc_8410_10_test(void)
{
const struct {
const char* b64;
const char *b64;
} rfc_8410_10[] = {
/* RFC 8410 - 10.2. Example X25519 Certificate */
{ "MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX"
@ -129,7 +129,8 @@ static int _rfc_8410_10_test(void)
"ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg"
"BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v"
"/BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg"
"w1AH9efZBw==" },
"w1AH9efZBw=="
},
};
unsigned n;
curve25519_key key;
@ -144,6 +145,40 @@ static int _rfc_8410_10_test(void)
return CRYPT_OK;
}
static int _x25519_pkcs8_test(void)
{
const struct {
const char *b64, *pass;
} _x25519_pkcs8[] = {
/* `openssl genpkey -algorithm x25519 -pass stdin -aes128` */
{
"MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjG5kRkEihOvQICCAAw"
"DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEHPLHLoCvesRyeToyMtGHWcEQM1+"
"FMpSO5DplX3d+YGTAvf0WxWaBff1q4bfKDn/7IoWQT1e4Fe6Psj62Vy9T69o3+Uy"
"VM6mdIOXGOkAtaMSsSk=",
"123456"
},
/* `openssl genpkey -algorithm x25519 -pass stdin` */
{
"MC4CAQAwBQYDK2VuBCIEIEAInaUdx+fQFfghpCzw/WdItRT3+FnPSkrU9TcIZTZW",
NULL
},
};
unsigned n;
curve25519_key key;
unsigned char buf[1024];
unsigned long buflen, passlen;
for (n = 0; n < sizeof(_x25519_pkcs8)/sizeof(_x25519_pkcs8[0]); ++n) {
buflen = sizeof(buf);
DO(base64_decode(_x25519_pkcs8[n].b64, strlen(_x25519_pkcs8[n].b64), buf, &buflen));
if (_x25519_pkcs8[n].pass != NULL) passlen = strlen(_x25519_pkcs8[n].pass);
else passlen = 0;
DO(x25519_import_pkcs8(buf, buflen, _x25519_pkcs8[n].pass, passlen, &key));
zeromem(buf, sizeof(buf));
}
return CRYPT_OK;
}
static int _x25519_compat_test(void)
{
curve25519_key priv, pub, imported;
@ -158,7 +193,7 @@ static int _x25519_compat_test(void)
DO(x25519_make_key(&yarrow_prng, prng_idx, &priv));
DO(x25519_export(buf, &buflen, PK_PRIVATE, &priv));
DO(x25519_import(buf, buflen, &imported));
DO(x25519_import_pkcs8(buf, buflen, NULL, 0, &imported));
DO(do_compare_testvector(&priv, sizeof(priv), &imported, sizeof(imported), "priv after ex-&import", __LINE__));
XMEMSET(&imported, 0, sizeof(imported));
@ -201,6 +236,9 @@ int x25519_test(void)
if ((ret = _rfc_8410_10_test()) != CRYPT_OK) {
return ret;
}
if ((ret = _x25519_pkcs8_test()) != CRYPT_OK) {
return ret;
}
if ((ret = _x25519_compat_test()) != CRYPT_OK) {
return ret;
}