Skip to content

Commit 7cc84d3

Browse files
committed
fix SetAsymKeyDer to set PKCS#8 version=1 when bundling publicKey (RFC 5958)
1 parent 4b00525 commit 7cc84d3

14 files changed

Lines changed: 547 additions & 10 deletions

tests/api/api.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,5 +252,77 @@ typedef struct testVector {
252252

253253
extern int testDevId;
254254

255+
/* Skip past outer SEQ header of a PKCS#8 / RFC 5958 OneAsymmetricKey DER blob,
256+
* return version byte (0=v1, 1=v2) or -1 on malformed input. */
257+
#ifdef WC_ENABLE_ASYM_KEY_EXPORT
258+
static WC_INLINE int test_pkcs8_get_version_byte(const byte* der, word32 derSz)
259+
{
260+
word32 idx = 0;
261+
word32 nBytes = 0;
262+
263+
/* SEQ tag + short len + INT tag + INT len + version = 5 */
264+
if (der == NULL || derSz < 5) {
265+
return -1;
266+
}
267+
/* SEQUENCE */
268+
if (der[idx++] != 0x30) {
269+
return -1;
270+
}
271+
if (idx >= derSz) {
272+
return -1;
273+
}
274+
if ((der[idx] & 0x80) == 0) {
275+
/* short-form length */
276+
idx += 1;
277+
}
278+
else {
279+
/* long-form length */
280+
nBytes = (word32)(der[idx] & 0x7F);
281+
if (nBytes == 0 || nBytes > 4) {
282+
return -1;
283+
}
284+
idx += (1 + nBytes);
285+
}
286+
if (idx + 3 > derSz) {
287+
return -1;
288+
}
289+
/* INTEGER, len 1 */
290+
if (der[idx] != 0x02 || der[idx + 1] != 0x01) {
291+
return -1;
292+
}
293+
294+
return (int)der[idx + 2];
295+
}
296+
297+
/* Overwrite OneAsymmetricKey version byte. Return the patched offset or
298+
* -1 on malformed input. */
299+
static WC_INLINE int test_pkcs8_patch_version_byte(byte* der, word32 derSz,
300+
byte newVer)
301+
{
302+
word32 idx = 0;
303+
word32 nBytes = 0;
304+
305+
if (der == NULL || derSz < 5 || der[idx++] != 0x30) {
306+
return -1;
307+
}
308+
if ((der[idx] & 0x80) == 0) {
309+
idx += 1;
310+
}
311+
else {
312+
nBytes = (word32)(der[idx] & 0x7F);
313+
if (nBytes == 0 || nBytes > 4) {
314+
return -1;
315+
}
316+
idx += (1 + nBytes);
317+
}
318+
if (idx + 3 > derSz || der[idx] != 0x02 || der[idx + 1] != 0x01) {
319+
return -1;
320+
}
321+
der[idx + 2] = newVer;
322+
323+
return (int)(idx + 2);
324+
}
325+
#endif /* WC_ENABLE_ASYM_KEY_EXPORT */
326+
255327
#endif /* WOLFCRYPT_TEST_API_H */
256328

tests/api/test_asn.c

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@
2121

2222
#include <tests/unit.h>
2323

24+
#include <tests/api/api.h>
2425
#include <tests/api/test_asn.h>
2526

2627
#include <wolfssl/wolfcrypt/asn.h>
28+
#include <wolfssl/wolfcrypt/asn_public.h>
29+
#include <wolfssl/wolfcrypt/random.h>
2730
#include <wolfssl/wolfcrypt/rsa.h>
31+
#ifdef HAVE_ED25519
32+
#include <wolfssl/wolfcrypt/ed25519.h>
33+
#endif
2834

2935
#if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519)
3036
static int test_SetAsymKeyDer_once(byte* privKey, word32 privKeySz, byte* pubKey,
@@ -55,6 +61,9 @@ int test_SetAsymKeyDer(void)
5561
#if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519)
5662
/* We can't access the keyEd25519Oid variable, so declare it instead */
5763
byte algId[] = {43, 101, 112};
64+
/* RFC 5958: version is v1 (0) for private only, v2 (1) when public key
65+
* bundled. Conditions 1-5 are private only, 6-8 include pub key and
66+
* mutate version[0] = 0x1 before building trueDer. */
5867
byte version[] = {0x0};
5968
byte keyPat = 0xcc;
6069

@@ -286,6 +295,7 @@ int test_SetAsymKeyDer(void)
286295
privKeySz = 32;
287296
pubKeySz = 32;
288297
trueDerSz = 82;
298+
version[0] = 0x1; /* publicKey present (v2) */
289299

290300
/* SEQ */
291301
trueDer[0] = ASN_SEQUENCE | ASN_CONSTRUCTED;
@@ -328,6 +338,7 @@ int test_SetAsymKeyDer(void)
328338
privKeySz = 32;
329339
pubKeySz = 128;
330340
trueDerSz = 180;
341+
version[0] = 0x1; /* publicKey present (v2) */
331342

332343
/* SEQ */
333344
trueDer[0] = ASN_SEQUENCE | ASN_CONSTRUCTED;
@@ -372,6 +383,7 @@ int test_SetAsymKeyDer(void)
372383
privKeySz = 32;
373384
pubKeySz = 256;
374385
trueDerSz = 310;
386+
version[0] = 0x1; /* publicKey present (v2) */
375387

376388
/* SEQ */
377389
trueDer[0] = ASN_SEQUENCE | ASN_CONSTRUCTED;
@@ -413,6 +425,155 @@ int test_SetAsymKeyDer(void)
413425

414426
}
415427

428+
/* RFC 5958 leniency: parser must accept all four variants:
429+
* {v=0,v=1} x {publicKey absent, present}. */
430+
int test_DecodeAsymKey_lenient_versions(void)
431+
{
432+
EXPECT_DECLS;
433+
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \
434+
defined(HAVE_ED25519_KEY_IMPORT) && defined(WOLFSSL_KEY_GEN)
435+
ed25519_key key;
436+
ed25519_key parsed;
437+
WC_RNG rng;
438+
byte bundled[256]; /* v=1 + publicKey */
439+
byte privOnly[256]; /* v=0, no publicKey */
440+
byte tmp[256];
441+
int bundledSz = 0;
442+
int privOnlySz = 0;
443+
word32 idx;
444+
445+
XMEMSET(&key, 0, sizeof(key));
446+
XMEMSET(&parsed, 0, sizeof(parsed));
447+
XMEMSET(&rng, 0, sizeof(rng));
448+
449+
ExpectIntEQ(wc_InitRng(&rng), 0);
450+
ExpectIntEQ(wc_ed25519_init(&key), 0);
451+
ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &key), 0);
452+
453+
ExpectIntGT(bundledSz = wc_Ed25519KeyToDer(&key, bundled,
454+
(word32)sizeof(bundled)), 0);
455+
ExpectIntGT(privOnlySz = wc_Ed25519PrivateKeyToDer(&key, privOnly,
456+
(word32)sizeof(privOnly)), 0);
457+
458+
if (EXPECT_SUCCESS() &&
459+
((bundledSz > 0) && ((size_t)bundledSz <= sizeof(bundled)) &&
460+
(privOnlySz > 0) && ((size_t)privOnlySz <= sizeof(privOnly)))) {
461+
462+
/* v=1 + publicKey */
463+
XMEMCPY(tmp, bundled, (size_t)bundledSz);
464+
XMEMSET(&parsed, 0, sizeof(parsed));
465+
ExpectIntEQ(wc_ed25519_init(&parsed), 0);
466+
idx = 0;
467+
ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed,
468+
(word32)bundledSz), 0);
469+
wc_ed25519_free(&parsed);
470+
471+
/* v=0 + publicKey: patch version byte, [1] publicKey field present. */
472+
XMEMCPY(tmp, bundled, (size_t)bundledSz);
473+
ExpectIntGT(test_pkcs8_patch_version_byte(tmp, (word32)bundledSz, 0),
474+
0);
475+
XMEMSET(&parsed, 0, sizeof(parsed));
476+
ExpectIntEQ(wc_ed25519_init(&parsed), 0);
477+
idx = 0;
478+
ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed,
479+
(word32)bundledSz), 0);
480+
wc_ed25519_free(&parsed);
481+
482+
/* v=0, no publicKey */
483+
XMEMCPY(tmp, privOnly, (size_t)privOnlySz);
484+
XMEMSET(&parsed, 0, sizeof(parsed));
485+
ExpectIntEQ(wc_ed25519_init(&parsed), 0);
486+
idx = 0;
487+
ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed,
488+
(word32)privOnlySz), 0);
489+
wc_ed25519_free(&parsed);
490+
491+
/* v=1, no publicKey */
492+
XMEMCPY(tmp, privOnly, (size_t)privOnlySz);
493+
ExpectIntGT(test_pkcs8_patch_version_byte(tmp, (word32)privOnlySz, 1),
494+
0);
495+
XMEMSET(&parsed, 0, sizeof(parsed));
496+
ExpectIntEQ(wc_ed25519_init(&parsed), 0);
497+
idx = 0;
498+
ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed,
499+
(word32)privOnlySz), 0);
500+
wc_ed25519_free(&parsed);
501+
}
502+
503+
wc_ed25519_free(&key);
504+
wc_FreeRng(&rng);
505+
#endif
506+
return EXPECT_RESULT();
507+
}
508+
509+
int test_DecodeAsymKey_negative(void)
510+
{
511+
EXPECT_DECLS;
512+
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \
513+
defined(HAVE_ED25519_KEY_IMPORT) && defined(WOLFSSL_KEY_GEN)
514+
ed25519_key key;
515+
ed25519_key parsed;
516+
WC_RNG rng;
517+
byte good[256];
518+
byte tmp[256];
519+
int goodSz = 0;
520+
word32 idx;
521+
522+
XMEMSET(&key, 0, sizeof(key));
523+
XMEMSET(&parsed, 0, sizeof(parsed));
524+
XMEMSET(&rng, 0, sizeof(rng));
525+
526+
ExpectIntEQ(wc_InitRng(&rng), 0);
527+
ExpectIntEQ(wc_ed25519_init(&key), 0);
528+
ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &key), 0);
529+
ExpectIntGT(goodSz = wc_Ed25519KeyToDer(&key, good,
530+
(word32)sizeof(good)), 0);
531+
532+
if (EXPECT_SUCCESS() &&
533+
(goodSz > 0 && (size_t)goodSz <= sizeof(good))) {
534+
535+
/* Truncated buffer */
536+
XMEMCPY(tmp, good, (size_t)goodSz);
537+
ExpectIntEQ(wc_ed25519_init(&parsed), 0);
538+
idx = 0;
539+
ExpectIntLT(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed,
540+
(word32)(goodSz - 1)), 0);
541+
wc_ed25519_free(&parsed);
542+
543+
/* Outer length too big. Patch low-order length byte (long form: bump
544+
* the last byte of the multi-byte length encoding). */
545+
XMEMCPY(tmp, good, (size_t)goodSz);
546+
if ((good[1] & 0x80) == 0) {
547+
tmp[1] = (byte)(good[1] + 1);
548+
}
549+
else {
550+
word32 nBytes = (word32)(good[1] & 0x7F);
551+
tmp[1 + nBytes] = (byte)(good[1 + nBytes] + 1);
552+
}
553+
XMEMSET(&parsed, 0, sizeof(parsed));
554+
ExpectIntEQ(wc_ed25519_init(&parsed), 0);
555+
idx = 0;
556+
ExpectIntLT(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed,
557+
(word32)goodSz), 0);
558+
wc_ed25519_free(&parsed);
559+
560+
/* Outer tag not SEQUENCE */
561+
XMEMCPY(tmp, good, (size_t)goodSz);
562+
tmp[0] = 0x02;
563+
XMEMSET(&parsed, 0, sizeof(parsed));
564+
ExpectIntEQ(wc_ed25519_init(&parsed), 0);
565+
idx = 0;
566+
ExpectIntLT(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed,
567+
(word32)goodSz), 0);
568+
wc_ed25519_free(&parsed);
569+
}
570+
571+
wc_ed25519_free(&key);
572+
wc_FreeRng(&rng);
573+
#endif
574+
return EXPECT_RESULT();
575+
}
576+
416577
#ifndef NO_ASN
417578
static int test_GetSetShortInt_once(word32 val, byte* valDer, word32 valDerSz)
418579
{

tests/api/test_asn.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <tests/api/api_decl.h>
2626

2727
int test_SetAsymKeyDer(void);
28+
int test_DecodeAsymKey_lenient_versions(void);
29+
int test_DecodeAsymKey_negative(void);
2830
int test_GetSetShortInt(void);
2931
int test_wc_IndexSequenceOf(void);
3032
int test_wolfssl_local_MatchBaseName(void);
@@ -34,6 +36,8 @@ int test_wc_DecodeObjectId(void);
3436

3537
#define TEST_ASN_DECLS \
3638
TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \
39+
TEST_DECL_GROUP("asn", test_DecodeAsymKey_lenient_versions), \
40+
TEST_DECL_GROUP("asn", test_DecodeAsymKey_negative), \
3741
TEST_DECL_GROUP("asn", test_GetSetShortInt), \
3842
TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf), \
3943
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName), \

tests/api/test_curve25519.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,61 @@ int test_wc_curve25519_priv_clamp_check(void)
608608
return EXPECT_RESULT();
609609
} /* END test_wc_curve25519_priv_clamp_check */
610610

611+
/*
612+
* RFC 5958 OneAsymmetricKey: version=v2 (1) when publicKey is bundled,
613+
* version=v1 (0) for private only.
614+
*/
615+
int test_wc_Curve25519KeyToDer_oneasymkey_version(void)
616+
{
617+
EXPECT_DECLS;
618+
#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) && \
619+
defined(HAVE_CURVE25519_KEY_IMPORT)
620+
curve25519_key key;
621+
curve25519_key key2;
622+
WC_RNG rng;
623+
byte ref[256]; /* reference DER (bundled, then private only) */
624+
byte rt[256]; /* re-export target for memcmp */
625+
int refSz = 0;
626+
int rtSz = 0;
627+
word32 idx;
628+
629+
XMEMSET(&key, 0, sizeof(key));
630+
XMEMSET(&key2, 0, sizeof(key2));
631+
XMEMSET(&rng, 0, sizeof(rng));
632+
633+
ExpectIntEQ(wc_InitRng(&rng), 0);
634+
ExpectIntEQ(wc_curve25519_init(&key), 0);
635+
ExpectIntEQ(wc_curve25519_init(&key2), 0);
636+
ExpectIntEQ(wc_curve25519_make_key(&rng, CURVE25519_KEYSIZE, &key), 0);
637+
638+
/* make_key sets both priv and pub: KeyToDer bundles both (v=1).
639+
* Use wc_Curve25519KeyDecode so the publicKey field is preserved in key2 */
640+
ExpectIntGT(refSz = wc_Curve25519KeyToDer(&key, ref,
641+
(word32)sizeof(ref), 1), 0);
642+
ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 1);
643+
idx = 0;
644+
ExpectIntEQ(wc_Curve25519KeyDecode(ref, &idx, &key2, (word32)refSz), 0);
645+
ExpectIntEQ(rtSz = wc_Curve25519KeyToDer(&key2, rt, (word32)sizeof(rt), 1),
646+
refSz);
647+
ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0);
648+
649+
/* Private only creates v=0. Reuse ref/rt. */
650+
XMEMSET(&key2, 0, sizeof(key2));
651+
ExpectIntEQ(wc_curve25519_init(&key2), 0);
652+
ExpectIntGT(refSz = wc_Curve25519PrivateKeyToDer(&key, ref,
653+
(word32)sizeof(ref)), 0);
654+
ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 0);
655+
idx = 0;
656+
ExpectIntEQ(wc_Curve25519PrivateKeyDecode(ref, &idx, &key2,
657+
(word32)refSz), 0);
658+
ExpectIntEQ(rtSz = wc_Curve25519PrivateKeyToDer(&key2, rt,
659+
(word32)sizeof(rt)), refSz);
660+
ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0);
661+
662+
wc_curve25519_free(&key);
663+
wc_curve25519_free(&key2);
664+
wc_FreeRng(&rng);
665+
#endif
666+
return EXPECT_RESULT();
667+
}
668+

tests/api/test_curve25519.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ int test_wc_curve25519_export_private_raw_ex(void);
3636
int test_wc_curve25519_import_private_raw_ex(void);
3737
int test_wc_curve25519_import_private(void);
3838
int test_wc_curve25519_priv_clamp_check(void);
39+
int test_wc_Curve25519KeyToDer_oneasymkey_version(void);
3940

4041
#define TEST_CURVE25519_DECLS \
4142
TEST_DECL_GROUP("curve25519", test_wc_curve25519_init), \
@@ -49,6 +50,7 @@ int test_wc_curve25519_priv_clamp_check(void);
4950
TEST_DECL_GROUP("curve25519", test_wc_curve25519_export_private_raw_ex), \
5051
TEST_DECL_GROUP("curve25519", test_wc_curve25519_import_private_raw_ex), \
5152
TEST_DECL_GROUP("curve25519", test_wc_curve25519_import_private), \
52-
TEST_DECL_GROUP("curve25519", test_wc_curve25519_priv_clamp_check)
53+
TEST_DECL_GROUP("curve25519", test_wc_curve25519_priv_clamp_check), \
54+
TEST_DECL_GROUP("curve25519", test_wc_Curve25519KeyToDer_oneasymkey_version)
5355

5456
#endif /* WOLFCRYPT_TEST_CURVE25519_H */

0 commit comments

Comments
 (0)