From 9b6cf56a6e481114f3ced24dd08b78acb2070c87 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 31 Aug 2021 16:28:24 -0700 Subject: [PATCH] Expanded support for Curve25519/Curve448 and TLS v1.3 sniffer (#4335) * Fixes for building with Ed/Curve25519 only. Fix for IoT safe demo to exit after running once. Added `WOLFSSL_DH_EXTRA` to `--enable-all` and `--enable-sniffer`. Cleanup uses of `==` in configure.ac. Various spelling fixes. * Fix for sniffer with TLS v1.3 session tickets. * Fix for ASN Template Ed25519 key export (missing version / not setting OID correctly). * Add key import/export support for Curve25519/Curve448. Refactor of the 25519/448 ASN code to combine duplicate code. * Refactor of Curve25519 code. Improved public key export to handle generation when only private is set. Improved private scalar buffer sizing. * Fix for static ephemeral loading of file buffer. * Added sniffer Curve25519 support and test case. * Fix for sniffer to not use ECC for X25519 if both are set. * Fix Curve448 public export when only private is set. * Fix for `dh_generate_test` for small stack size. * Reduce stack size use on new asymmetric DER import/export functions. Cleanup pub length calc. * Fix invalid comment. --- IDE/iotsafe/memory-tls.c | 1 + certs/statickeys/gen-static.sh | 18 +- certs/statickeys/include.am | 7 + certs/statickeys/x25519-pub.der | Bin 0 -> 44 bytes certs/statickeys/x25519-pub.pem | 3 + certs/statickeys/x25519.der | Bin 0 -> 48 bytes certs/statickeys/x25519.pem | 3 + configure.ac | 37 +- examples/server/server.c | 7 + scripts/include.am | 1 + scripts/sniffer-testsuite.test | 10 + scripts/sniffer-tls13-gen.sh | 73 +- scripts/sniffer-tls13-x25519.pcap | Bin 0 -> 42925 bytes src/internal.c | 19 +- src/ocsp.c | 2 +- src/sniffer.c | 154 ++- src/ssl.c | 54 +- src/tls.c | 21 +- src/tls13.c | 2 +- sslSniffer/sslSnifferTest/snifftest.c | 31 +- tests/api.c | 22 +- wolfcrypt/src/asn.c | 1302 +++++++++++-------------- wolfcrypt/src/curve25519.c | 264 +++-- wolfcrypt/src/curve448.c | 71 +- wolfcrypt/test/test.c | 118 ++- wolfssl/internal.h | 7 + wolfssl/wolfcrypt/asn.h | 26 +- wolfssl/wolfcrypt/asn_public.h | 89 +- wolfssl/wolfcrypt/curve25519.h | 20 +- wolfssl/wolfcrypt/curve448.h | 12 + wolfssl/wolfcrypt/ecc.h | 8 +- 31 files changed, 1353 insertions(+), 1029 deletions(-) create mode 100644 certs/statickeys/x25519-pub.der create mode 100644 certs/statickeys/x25519-pub.pem create mode 100644 certs/statickeys/x25519.der create mode 100644 certs/statickeys/x25519.pem create mode 100644 scripts/sniffer-tls13-x25519.pcap diff --git a/IDE/iotsafe/memory-tls.c b/IDE/iotsafe/memory-tls.c index e5f8036e8f..fd095b7051 100644 --- a/IDE/iotsafe/memory-tls.c +++ b/IDE/iotsafe/memory-tls.c @@ -384,6 +384,7 @@ static int server_loop(void) wolfSSL_CTX_free(srv_ctx); srv_ctx = NULL; server_state = 0; + return -1; /* exit test loop, so it doesn't keep running forever */ } } diff --git a/certs/statickeys/gen-static.sh b/certs/statickeys/gen-static.sh index dc92757db1..681a77edf5 100644 --- a/certs/statickeys/gen-static.sh +++ b/certs/statickeys/gen-static.sh @@ -3,13 +3,19 @@ # run from wolfssl root # SECP256R1 -openssl ecparam -name secp256r1 -genkey -noout -out certs/statickeys/ecc-secp256r1.pem -noout +openssl ecparam -name prime256v1 -genkey -noout -out certs/statickeys/ecc-secp256r1.pem openssl ec -inform pem -in certs/statickeys/ecc-secp256r1.pem -outform der -out certs/statickeys/ecc-secp256r1.der # DH 2048-bit (keySz = 29) # Using one generated and capture with wolfSSL using wc_DhGenerateKeyPair (openssl generates DH keys with 2048-bits... based on the DH "p" prime size) -#openssl genpkey -paramfile certs/statickeys/dh-ffdhe2048-params.pem -out certs/statickeys/dh-ffdhe2048.der -openssl pkey -inform der -in certs/statickeys/dh-ffdhe2048.der -outform pem -out certs/statickeys/dh-ffdhe2048.pem -# Export DH public key as DER and convert to PEM -openssl pkey -inform der -in certs/statickeys/dh-ffdhe2048.der -outform der -out certs/statickeys/dh-ffdhe2048-pub.der -pubout -openssl pkey -inform der -in certs/statickeys/dh-ffdhe2048.der -outform pem -out certs/statickeys/dh-ffdhe2048-pub.pem -pubout +openssl genpkey -paramfile certs/statickeys/dh-ffdhe2048-params.pem -outform -out certs/statickeys/dh-ffdhe2048.pem +openssl pkey -inform pem -in certs/statickeys/dh-ffdhe2048.pem -outform der -out certs/statickeys/dh-ffdhe2048.der +# Export DH public key as DER and PEM +openssl pkey -inform pem -in certs/statickeys/dh-ffdhe2048.pem -outform der -out certs/statickeys/dh-ffdhe2048-pub.der -pubout +openssl pkey -inform pem -in certs/statickeys/dh-ffdhe2048.pem -outform pem -out certs/statickeys/dh-ffdhe2048-pub.pem -pubout + +# X25519 (Curve25519) +openssl genpkey -algorithm x25519 -outform pem -out certs/statickeys/x25519.pem +openssl pkey -inform pem -in certs/statickeys/x25519.pem -outform der -out certs/statickeys/x25519.der +openssl pkey -inform pem -in certs/statickeys/x25519.pem -outform der -out certs/statickeys/x25519-pub.der -pubout +openssl pkey -inform pem -in certs/statickeys/x25519.pem -outform pem -out certs/statickeys/x25519-pub.pem -pubout diff --git a/certs/statickeys/include.am b/certs/statickeys/include.am index 123764dd56..84641be109 100644 --- a/certs/statickeys/include.am +++ b/certs/statickeys/include.am @@ -17,3 +17,10 @@ EXTRA_DIST += \ certs/statickeys/dh-ffdhe2048.pem \ certs/statickeys/dh-ffdhe2048-pub.der \ certs/statickeys/dh-ffdhe2048-pub.pem + +# Curve25519 Keys +EXTRA_DIST += \ + certs/statickeys/x25519.der \ + certs/statickeys/x25519.pem \ + certs/statickeys/x25519-pub.der \ + certs/statickeys/x25519-pub.pem diff --git a/certs/statickeys/x25519-pub.der b/certs/statickeys/x25519-pub.der new file mode 100644 index 0000000000000000000000000000000000000000..c7458a3feb73f24932229ac0a6a3a7f0f10099f9 GIT binary patch literal 44 zcmXreGGJw6)=tf1R%GDZ({tRF_uwI>@L3sFf2&q3)&DTla>gsQMbQ?2r`^_40{}wI B5sd%< literal 0 HcmV?d00001 diff --git a/certs/statickeys/x25519-pub.pem b/certs/statickeys/x25519-pub.pem new file mode 100644 index 0000000000..19fab5bb05 --- /dev/null +++ b/certs/statickeys/x25519-pub.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VuAyEACbyMx0UNwcICV5poOv16qKUv8Jk5mOomols4/ZbbKiY= +-----END PUBLIC KEY----- diff --git a/certs/statickeys/x25519.der b/certs/statickeys/x25519.der new file mode 100644 index 0000000000000000000000000000000000000000..916a5d79765dd63f778ec71c6a244aa6da246020 GIT binary patch literal 48 zcmXreV`5}5U}a<0PR(OcVo|8*GmJ4_DS05ytU!9ni}wdRdp3(6tGv8MiDBKZ&D;4a E07d^1;Q#;t literal 0 HcmV?d00001 diff --git a/certs/statickeys/x25519.pem b/certs/statickeys/x25519.pem new file mode 100644 index 0000000000..92f96ca2b5 --- /dev/null +++ b/certs/statickeys/x25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VuBCIEIHiOMVwzqRnAXjZwG6To78GJjLMVxnnTrCIArvqztw94 +-----END PRIVATE KEY----- diff --git a/configure.ac b/configure.ac index 6a4a3e4908..312abb893f 100644 --- a/configure.ac +++ b/configure.ac @@ -491,13 +491,19 @@ then DEFAULT_MAX_CLASSIC_ASYM_KEY_BITS=4096 # Enable multiple attribute additions such as DC - AM_CFLAGS="-DWOLFSSL_MULTI_ATTRIB $AM_CFLAGS" + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_MULTI_ATTRIB" - # Enable AES Decrypt, AES ECB, Alt Names, DER Load, Keep Certs, CRL IO with Timeout - AM_CFLAGS="$AM_CFLAGS -DHAVE_AES_DECRYPT -DHAVE_AES_ECB -DWOLFSSL_ALT_NAMES -DWOLFSSL_DER_LOAD -DKEEP_OUR_CERT -DKEEP_PEER_CERT" + # Enable AES Decrypt, AES ECB + AM_CFLAGS="$AM_CFLAGS -DHAVE_AES_DECRYPT -DHAVE_AES_ECB" + + # Enable Alt Names, DER Load, Keep Certs, CRL IO with Timeout + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ALT_NAMES -DWOLFSSL_DER_LOAD -DKEEP_OUR_CERT -DKEEP_PEER_CERT" # Enable ECC Key Gen checks AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_VALIDATE_ECC_KEYGEN" + + # Enable DH Extra + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DH_EXTRA" fi @@ -577,7 +583,7 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_VALIDATE_ECC_IMPORT" fi - if test "$ENABLED_FIPS" == "no" + if test "$ENABLED_FIPS" = "no" then test "$enable_xchacha" = "" && enable_xchacha=yes test "$enable_ed25519" = "" && enable_ed25519=yes @@ -1003,8 +1009,8 @@ AC_ARG_ENABLE([opensslall], if test "$ENABLED_LIBWEBSOCKETS" = "yes" || test "$ENABLED_OPENVPN" = "yes" || \ test "$ENABLED_WPAS_DPP" = "yes" || test "$ENABLED_SMIME" = "yes" || \ test "$ENABLED_HAPROXY" = "yes" || test "$ENABLED_BIND" = "yes" || \ - test "$ENABLED_NTP" == "yes" || test "$ENABLED_NETSNMP" = "yes" || \ - test "$ENABLED_OPENRESTY" = "yes" || test "$ENABLED_RSYSLOG" == "yes" + test "$ENABLED_NTP" = "yes" || test "$ENABLED_NETSNMP" = "yes" || \ + test "$ENABLED_OPENRESTY" = "yes" || test "$ENABLED_RSYSLOG" = "yes" then ENABLED_OPENSSLALL="yes" fi @@ -1026,7 +1032,7 @@ if test "$ENABLED_OPENSSH" = "yes" || test "$ENABLED_NGINX" = "yes" || \ test "$ENABLED_SNIFFER" = "yes" || test "$ENABLED_OPENSSLALL" = "yes" || \ test "$ENABLED_LIBWEBSOCKETS" = "yes" || \ test "x$ENABLED_LIGHTY" = "xyes" || test "$ENABLED_LIBSSH2" = "yes" || \ - test "x$ENABLED_NTP" = "xyes" || test "$ENABLED_RSYSLOG" == "yes" + test "x$ENABLED_NTP" = "xyes" || test "$ENABLED_RSYSLOG" = "yes" then ENABLED_OPENSSLEXTRA="yes" fi @@ -2893,6 +2899,11 @@ else fi fi +if test "x$ENABLED_SNIFFER" = "xyes" && test "x$ENABLED_DH" = "xyes" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DH_EXTRA" +fi + # Anonymous AC_ARG_ENABLE([anon], @@ -2901,7 +2912,8 @@ AC_ARG_ENABLE([anon], [ ENABLED_ANON=no ] ) -if test "x$ENABLED_WPAS" = "xyes" || test "x$ENABLED_NGINX" = "xyes" || test "x$ENABLED_HAPROXY" = "xyes" || test "$ENABLED_RSYSLOG" == "yes" +if test "x$ENABLED_WPAS" = "xyes" || test "x$ENABLED_NGINX" = "xyes" || \ + test "x$ENABLED_HAPROXY" = "xyes" || test "$ENABLED_RSYSLOG" = "yes" then ENABLED_ANON=yes fi @@ -3593,7 +3605,7 @@ if test "$ENABLED_CRL" != "no" then AM_CFLAGS="$AM_CFLAGS -DHAVE_CRL" fi -if test "$ENABLED_CRL" == "io" +if test "$ENABLED_CRL" = "io" then AM_CFLAGS="$AM_CFLAGS -DHAVE_CRL_IO" fi @@ -6174,7 +6186,7 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TRACK_MEMORY -DWOLFSSL_DEBUG_MEMORY" fi -if test "x$ENABLED_MEMTEST" == "xfail" +if test "x$ENABLED_MEMTEST" = "xfail" then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_FORCE_MALLOC_FAIL_TEST" fi @@ -6386,8 +6398,9 @@ AS_IF([test "x$ENABLED_LEANTLS" = "xyes" && \ AS_IF([test "x$ENABLED_SNIFFER" = "xyes" && \ test "x$ENABLED_RSA" = "xno" && \ - test "x$ENABLED_ECC" = "xno"], - [AC_MSG_ERROR([please enable ecc or rsa if enabling sniffer.])]) + test "x$ENABLED_ECC" = "xno" && \ + test "x$ENABLED_CURVE25519" = "xno"], + [AC_MSG_ERROR([please enable ecc, rsa or curve25519 if enabling sniffer.])]) # Lean TLS forces off prereqs of SCEP. AS_IF([test "x$ENABLED_SCEP" = "xyes" && \ diff --git a/examples/server/server.c b/examples/server/server.c index e671253183..ec95f76590 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -2191,6 +2191,13 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) err_sys_ex(runWithErrors, "error loading static DH key"); } #endif +#ifdef HAVE_CURVE25519 + ret = wolfSSL_CTX_set_ephemeral_key(ctx, WC_PK_TYPE_CURVE25519, + "./certs/statickeys/x25519.pem", 0, WOLFSSL_FILETYPE_PEM); + if (ret != 0) { + err_sys_ex(runWithErrors, "error loading static X25519 key"); + } +#endif #endif /* WOLFSSL_SNIFFER && WOLFSSL_STATIC_EPHEMERAL */ if (cipherList && !useDefCipherList) { diff --git a/scripts/include.am b/scripts/include.am index dc47aad84e..2d2ca79481 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -91,6 +91,7 @@ EXTRA_DIST += scripts/testsuite.pcap \ scripts/sniffer-ipv6.pcap \ scripts/sniffer-tls13-dh.pcap \ scripts/sniffer-tls13-ecc.pcap \ + scripts/sniffer-tls13-x25519.pcap \ scripts/sniffer-tls13-gen.sh \ scripts/sniffer-tls13-hrr.pcap \ scripts/ping.test \ diff --git a/scripts/sniffer-testsuite.test b/scripts/sniffer-testsuite.test index 083974bcfa..d01bf6e8b0 100755 --- a/scripts/sniffer-testsuite.test +++ b/scripts/sniffer-testsuite.test @@ -39,6 +39,16 @@ then [ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 DH\n" && exit 1 fi +# TLS v1.3 sniffer test X25519 (and resumption) +if test $# -ne 0 +then + ./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-x25519.pcap ./certs/statickeys/x25519.pem 127.0.0.1 11111 + + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 X25519\n" && exit 1 +fi + + # TLS v1.3 sniffer test hello_retry_request (HRR) with ECDHE if test $# -ne 0 then diff --git a/scripts/sniffer-tls13-gen.sh b/scripts/sniffer-tls13-gen.sh index a98e7b4d4a..b3421a7859 100755 --- a/scripts/sniffer-tls13-gen.sh +++ b/scripts/sniffer-tls13-gen.sh @@ -7,23 +7,64 @@ # Script to generate wireshark trace for sniffer-tls13-dh.pcap #./configure --enable-sniffer --enable-session-ticket --disable-ecc && make -# TLS v1.3 -./examples/server/server -v 4 -l TLS13-AES128-GCM-SHA256 & -./examples/client/client -v 4 -l TLS13-AES128-GCM-SHA256 -./examples/server/server -v 4 -l TLS13-AES256-GCM-SHA384 & -./examples/client/client -v 4 -l TLS13-AES256-GCM-SHA384 -./examples/server/server -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 & -./examples/client/client -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 +# Run: with dh or ecc +if [ "$1" == "dh" ] || [ "$1" == "ecc" ]; then + # TLS v1.3 + ./examples/server/server -v 4 -l TLS13-AES128-GCM-SHA256 & + ./examples/client/client -v 4 -l TLS13-AES128-GCM-SHA256 + ./examples/server/server -v 4 -l TLS13-AES256-GCM-SHA384 & + ./examples/client/client -v 4 -l TLS13-AES256-GCM-SHA384 + ./examples/server/server -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 & + ./examples/client/client -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -# TLS v1.3 Resumption -./examples/server/server -v 4 -l TLS13-AES128-GCM-SHA256 -r & -./examples/client/client -v 4 -l TLS13-AES128-GCM-SHA256 -r -./examples/server/server -v 4 -l TLS13-AES256-GCM-SHA384 -r & -./examples/client/client -v 4 -l TLS13-AES256-GCM-SHA384 -r -./examples/server/server -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -r & -./examples/client/client -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -r + # TLS v1.3 Resumption + ./examples/server/server -v 4 -l TLS13-AES128-GCM-SHA256 -r & + ./examples/client/client -v 4 -l TLS13-AES128-GCM-SHA256 -r + ./examples/server/server -v 4 -l TLS13-AES256-GCM-SHA384 -r & + ./examples/client/client -v 4 -l TLS13-AES256-GCM-SHA384 -r + ./examples/server/server -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -r & + ./examples/client/client -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -r +fi + +# Script to generate wireshark trace for sniffer-tls13-x25519.pcap +#./configure --enable-sniffer --enable-session-ticket --enable-curve25519 --disable-dh --disable-ecc && make +# Run: with x25519 +if [ "$1" == "x25519" ]; then + # TLS v1.3 + ./examples/server/server -v 4 -l TLS13-AES128-GCM-SHA256 -c ./certs/ed25519/server-ed25519.pem -k ./certs/ed25519/server-ed25519-priv.pem -A ./certs/ed25519/client-ed25519.pem & + sleep 0.1 + ./examples/client/client -v 4 -l TLS13-AES128-GCM-SHA256 -c ./certs/ed25519/client-ed25519.pem -k ./certs/ed25519/client-ed25519-priv.pem -A ./certs/ed25519/root-ed25519.pem + + ./examples/server/server -v 4 -l TLS13-AES256-GCM-SHA384 -c ./certs/ed25519/server-ed25519.pem -k ./certs/ed25519/server-ed25519-priv.pem -A ./certs/ed25519/client-ed25519.pem & + sleep 0.1 + ./examples/client/client -v 4 -l TLS13-AES256-GCM-SHA384 -c ./certs/ed25519/client-ed25519.pem -k ./certs/ed25519/client-ed25519-priv.pem -A ./certs/ed25519/root-ed25519.pem + + ./examples/server/server -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -c ./certs/ed25519/server-ed25519.pem -k ./certs/ed25519/server-ed25519-priv.pem -A ./certs/ed25519/client-ed25519.pem & + sleep 0.1 + ./examples/client/client -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -c ./certs/ed25519/client-ed25519.pem -k ./certs/ed25519/client-ed25519-priv.pem -A ./certs/ed25519/root-ed25519.pem + + # TLS v1.3 Resumption + ./examples/server/server -v 4 -l TLS13-AES128-GCM-SHA256 -r -c ./certs/ed25519/server-ed25519.pem -k ./certs/ed25519/server-ed25519-priv.pem -A ./certs/ed25519/client-ed25519.pem & + sleep 0.1 + ./examples/client/client -v 4 -l TLS13-AES128-GCM-SHA256 -r -c ./certs/ed25519/client-ed25519.pem -k ./certs/ed25519/client-ed25519-priv.pem -A ./certs/ed25519/root-ed25519.pem + + ./examples/server/server -v 4 -l TLS13-AES256-GCM-SHA384 -r -c ./certs/ed25519/server-ed25519.pem -k ./certs/ed25519/server-ed25519-priv.pem -A ./certs/ed25519/client-ed25519.pem & + sleep 0.1 + ./examples/client/client -v 4 -l TLS13-AES256-GCM-SHA384 -r -c ./certs/ed25519/client-ed25519.pem -k ./certs/ed25519/client-ed25519-priv.pem -A ./certs/ed25519/root-ed25519.pem + + ./examples/server/server -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -r -c ./certs/ed25519/server-ed25519.pem -k ./certs/ed25519/server-ed25519-priv.pem -A ./certs/ed25519/client-ed25519.pem & + sleep 0.1 + ./examples/client/client -v 4 -l TLS13-CHACHA20-POLY1305-SHA256 -r -c ./certs/ed25519/client-ed25519.pem -k ./certs/ed25519/client-ed25519-priv.pem -A ./certs/ed25519/root-ed25519.pem +fi # TLS v1.3 Hello Retry Request (save this as sniffer-tls13-hrr.pcap) # ./configure --enable-sniffer CFLAGS="-DWOLFSSL_SNIFFER_WATCH" --disable-dh && make -./examples/server/server -v 4 -i -x -g & -./examples/client/client -v 4 -J + +# Run ./scripts/sniffer-tls13-gen.sh hrr +if [ "$1" == "hrr" ]; then + # TLS v1.3 Hello Retry Request + ./examples/server/server -v 4 -i -x -g & + sleep 0.1 + + ./examples/client/client -v 4 -J +fi diff --git a/scripts/sniffer-tls13-x25519.pcap b/scripts/sniffer-tls13-x25519.pcap new file mode 100644 index 0000000000000000000000000000000000000000..193271282fd0117e8559342bef1769fa919b1d62 GIT binary patch literal 42925 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

Tbmmz{nWpItbS&v;vF>m%As3rWg4nm zvo@4gneWs+PyJsH|CVc2n;Ph8FD` zx~j>q1Q_S)e|~uG$iuG*ciLV*zxg!hds>8l-1&VsQv0V}W1e4n?w_mW)@{ol1efmB z*my*NA?yBu4N8}VjSN;i{Jz7PXLbI~FQ+OQ*2=0*PUG;qdNFa+&q<5JtTVdr%ZJQu zG#BWta$Ge<^7GEv%20_}*Xrg}c&7>)E0}Uft9dL|&-@n0czAmBqflPEnum!)jt1jSR!zrEiO;}9n-x%9=0 zbLU!$$h^0TOp`qtr-xPiJ=WCsCHJr4-qgu&SK38{)YVQB`oHeo6$Mw1 zgyWYL*M)ADI&^u#c8Nz{3R6L@>}O^s*!@THEok@bE!YP3mNi==K`sTEGoO{2U=a@W zZ3f)8&>|dU2FNUsZ%vJ+B`D`do>zEr$ZO)4L%075Fnnu@*z;x2mX?{5S6%h-b+WQ*$#3A$ecUu%!F4VgNtybMR1=$ zhmem|ZaWAv3*-~|lcL45rk%5UdNwNcuweSzUn?6j?CpFf2u^*HwoFVW_j`P?x>Rar2hcCav7R-@WIo?yx#_U(090rX7zy9zWHj?0R({$FrzEHFFK* z`vu;#CqEK@voBHKsb465=b`0ydt2WoHXZ3%W^-KmN6e#t^IZf@njD{1FASa5>ZMX@ zerC#|i1g<^ewWl2YHZm7ZiWQ%6Anda+B?=HlynDq81`7@wnLy00EOaWL1toBA%jBk z4m=d0L&z;_w#I!-g59K$k@4{c7*tKD}v3{ zcs@C{9%SblK~mhF%z@_i!ytP>=BNuX6ElPy(Ik}20e3re2pMF?vC3_SL2eiRJNx}+ z;SS-B2^L|wTz@__3C3;&nXfBEk~9~Y)2zv=3m|zu-@@L=i|V`6|0-`CK(X&e}Ceoa0W$%OUXu;T=r~=(eLCb@V=B8 za8K6qJ<96 z2{7zLPXZ|BNJu>Ww-%BFx@`}tg3JL8lQ%FiFw`(JXMJIdSDjiH@zU_}rzn+%d7X16 z=*_b;-QZ_y)@Wi>+e0u3fQHdc7&O7BSCDjSG{`eEB?$)#Bt3ssWvzKM4S9f?alPeq zkcU9#^vN<4Gnfv_`_n3(d{u1#1qyU9z1#Mn8pteAStsthgKLSv|FgP$JKRh6$v7Eh zXVg4n@OTcgV}>kAZrz0D))^psK<2zxB;1~aM&Ks6TcPdAZrg+EjO#6DfZS>t6;a5h zu5;L@fI(rI)RS2~%Upiyzk2EO`AW9BXla6mC>w)#Nv^BatgAGH*UV-JE{ z@Jp4jPoQD>rsB!h_yz%qCrFt}1LO*jIk}q54Dk#MU<}Pv(dd~9>XUfZnnw_y_(L)k z$c%2=gBqYXQF?dCN<&pmRvaf>u-`3C@JSk4B9~Xrhem}6?PryZBW1XIvOX{&-oT+P*+3Wt@J#ok7 z(f+Axz9l{v+9^^U0*bVLZNi}ljkGq_n#Z7S5xhiYTyHrW6apY~PU{lYIF1CFQSsy} zBov{IV~`mjvp}J!UORDrVd>&!nJJZiUNa?4Y?f~5zjMJrHFjIY>2G_=lh4#TE&KfG zfDy>ftGXn)9dv9kJOzO41KA5QXPO>Sjbo78&%xaeZ5)Hl0GS1HyYR<+g#*SmYuyfh zTG@3~DMZ(|tpjBKY(0|P`3yOOfy@Tk0WybKpRhTQjQaQ)*qwqX85NXL!ChfaBFsUG zbI_bV)^J;)PdMCA%mL*bXt*)1x10m^7c(=1@QbMX!8;G6+|?0vx|PEHn%yF!5#--3 z`b0I3LGkh&)SyCc9H%~JVE}E<17T=kgn0^Fly>?{Z2{2o4F+tDV{5BC&>C$9SVQ(0 z5oT(q|B3QCP%rROTR<0Em>RA-R6Ff-3RW+6Dh@(1EhU9SW6D2-!~IUw^uVReNF zbD(Y$1i1~laSSp8WERMrt4~-6udhIM57PPykQpGeKrJKa`ilHN*MAmGUy-A+xM{=2 zuwS;PE=lqxulA_^Vc>oyduq)c(E19rQ{e7vIsAUGUrx2fE$me-TccO-9e<9RM_Us( zm5rjGN=_~yxz!8u*4-zBgBdA!Ku3ncFA@QH3FI}9ITN0-5VO7llmtM*ya#!G1T&hV%ArH+}ZvDXMoIk@tK8~^%Wqm9EW)Yw!Y$1 z#Z*_2S>W{*iPxEX&tG`9+UtG9XRnhEztp~6Su0vuyYRu@U9k)QF4RdkXG&VkQT(T; zHi1+B@bV{hi?&Q?h$|SBq?5$41EoIc|PG?Q=L|4lh1%vS{kf z-gYM6?1v9^)z%p`%=*`&`TE(VsWmlR$fsbb1Jy@1hljrAk%*^pW_QpPU;`$@d(o_0xBmY&~IZ`pSRXGVxuVWV8Om==E?UhHfWS3+c1RG5}`+l{{ zrR2c6?~X6Fa6imzJ>UNPxbCLihcoWAEPAxZXD<8ula+S$b6wQSx9M_e@4H{S#l!h# z>0w(>&6MUXQ%+szxbpDo;eFy)ElcjG?N2yZp?7S4^l~Yamwrz&GUS}^7HdxV$zNFh zRGwv$$iq(ke6eGPmKwEe6k1Zutv=&O){cua7F<8Vk~ued`N^h^^$#B0YOI;_<+wZJZ^uRH|c6l)f9s0`94^7xA$2 z9}P$=6~3?f@!EbZqbpbB%ik<2b&J+J*BZBG0>}K0O+JQv>m(1|mS+;{IDA<%Lg3VS z_t^`c^85?Bs2J~SwD7^Z+;d&AT|3W4p7i7~T%NTkh+)-+kC(0#hb`4S#=sRkC*QkH z!a7QP*71$=Y;Nv8v+8MC_t*WGgf(R)I1`ey|4Ev+_v$;3R_{(;p|;s=c}q6u-RYkh2fVp>@`OZv5Wv+2^hj25APjcU6}RKt%vb5@UE zA-3PjxF^c+*!FjP?>Eg)h}&(Pci`fR<*(L>ChnV)^;LLvz~hAmdDd(--o2)i_&2}g zbr%cr{irzoL9t9uc6eLUu4Ma|q~j@5q_=N4Jk7fzJ)ho#uvAHvaFktwl=pedVbaQl1YkL-S5x3Twj;f zp#4tdXUmJDD_-Zcm3td4U+_owg!h!5v#Ywq3Juv+_>Y4M&M%)yDS%scAg`~mw#u6U zDm+2v%>K?oaB>dPTxi(=D}et&FB17wG1U!Z7N{CAeNwb!Z7qi&=nWp~i zod0>5&E?z^36dqn!Jk{AL_!*0toH)Ba@luMeA_C9yuQNPDsLvpr66(|(?Jtda-5 zPs(2W_5uGN$%SWF9ypfyKeJu!`z+k*_~pDCq9L-cSYM0h|ICip61G+CwmqH>a#6s4 zl6(d_HyeJjh_zMTY>=x!<|Om65?*fsY0d8OI?$TXfOHC+wN>6cusNXh7B4+@S7{%; zDSvSK^LNYFX9n&%`2Tdl^XZ?MFJ~@tQI}^9O;S91KRHO`=Xa@Z2U~lp3pt}&%?0kx z%|2pf_?Ib2U~N@8r(1FTmvmKL&4o9Nis!sr{7LXD&s(+nV=UK9dQ8`H8%Wj7Uan(a z_WMxN^)Rmj*1yVJ2fzA$-TtZDbN24<%S!ij|8?KBpQUoeQLj~7nU7pqTG71d{r%bZ ze>(5o{~(S1t>yF}?f;?2T2{0r)C(T|=#WyQQOk6G@7d6f_T%-EJE~g$>{q^Y{mIhU zL#lOaPD1v;|vQ64qN)C{Mb z-kUhjOK`G^{Yl4}Yc(t7`_JqUk*<}kFIEsVH(h)461&g&jNOyJ#Bdic&HXh=xbOJ8 zS-A_h|1m$`o96W3*^ez7lzX{;f0!Vkd%M^Dpy0pP>}QiNr}s||wD3vRJ~}b+Lef<0 zOHQsuIx4L!C$w*|rABjp)Dul-`6qrwP{DV}&!=zfH$4;BqcbCZ&ST9rj_aklc5sNc z%)jq;xq0jFw>^h)9~>!P5?3O9<^PA@J2Ku>3h>zkekz$8-)$9CxgDbY~WoJt3`lshXz|-%<0i=5nw;gPaAgzUFIwR`1ENzigBiZ@PYRt>6$eE`QGDsDb-@F7P()3v3^?6WRoB<@7dW1;eY>jMj9`XeCA-|*ZZXX z)9u#qO_@hm7Cs0Jd?^wnQEK`&X}Wskl2ez1NeC z8H~25HlziebA1<6T^`x()w16vKkQuaYKLR<4lHf4h|g~mxx#2s##($b{k8fAhx6b6 zn>_VnSoVIwj{_TDl?pN`^(_1G;LReN?Q1@sT6;)bs%#ebiQSziYG)ij_G3rD{4{He zm3#O8xLmmS^Zx?r&vK8xpXcgpotB|qeOhCy!2Xl|#cvk>-155i$z^`Q-DZE%Raq=9 z2i{ak?zQro;<5Fx*RH7w>xvm;?{(h+9IHXJ>eINe5#o04a&wKbIu5|5-idoT_O>4SWy5g(m`f`%mNj1k=K6j ziWKWAeUxIneExUQqZ(JQ+A}HtOT1hZu~V&JhwgpdBtBQO4Q8q*SsuQQO9|6^yTG#U zRD*rus-I`qMnvtnk``F;Y%+gw+9uuft*kXBQHs^JEvr*?JJ>+3yd+4HZ$T$!!Pi@W zTnKV0$eeT$R)R%3)VEXNzJ(U))>e7*L1uw`>#D{OtzI@Kc+2&ld*3?!_EAh~?XIh? z+pPQCz2(c9774Ye;PrPam&xu)Vs&#%-QR4l(R5!^e5u=q4Tl#TFs**H_uK5-`wKuW zDia~eXP|>UZ@@;GKUGZi0l5lfj-D7R;q@2bB3$LGxXed+Hq!L!f2BfG z&-0ou!SjvkbFJPbr%Et|KUcTT(Kw&>aalNjQm|^j>+6FbbGTft-TS(&@j=5|E9ZjA zLZJ(et<>dA$S9fJe9OH%N%_0_%O&TRcFj5Z(dO{p+{{;#ud7Zj*d`~sQvZ#@yIhd( z=g1NcMMzo{W%fGI25P3jPl2`tWh7jSn9Y9u-mVGdH9gSKtLFFpbJ6=W93Un>;|hZ~AH zNb4`)=75HnFV5mFJoI+k?SDF)(*K`yaCo{s0-3){ft8r`7od2FMjv8sRb(YR#2l@i zUf#8T6G$(%A?CIFzkueU;X};Vi7-<;{ZI7T_er+pUHdm<8)6o0-*g0IE^LVTjw0bW z{R465=C$vW?2w0sLFRzW1BKNCBFurh%?{)?WOG1ffXo7!Bd5a3u$z$qj3M0(#_bsM zgvjmzt-n|enI~Mk|I0p*S)lb7b&L#*Rm{wG8FoDa+Z}4XzYB0(UN7=#@9_!A&rDC2 zIWF$$w0dXpNa1);SL_Gh+JxZ*_yeAOP}R( zn|eRbjMF<7*PkiHHduc~IcDDud<$E)?##PyNq z;(z?E6nr>sZ;gJzk+84>f4{J}{d9NT@%5&O%YouoZGMJVew^Fb#MP4BOUVBzR+E?+ z3=Ook!~gwR^Dewozp*ei=Sru~BuUZo`KRAi32w_cwkg9oFU`gJ{f30NdN~!ttj9lY zgQ8MJg>ceBB#DD&$m=l#+czBrMJLFdBz0EsS`1KLfFud5>oGvd-mbjspc#1S0dzeE z%pB0LI&?jTgHWr#3x5H>^ZzL4UBADZ-;DC^OA%hy92sF%@m1CO3Be=*x_pJ~^%&{u zgaZYV-wv9seV=p~c|aQ!ARrHc0;N`mm6*YIP@o)!2MToXU9f%AagbS{Iz!yvl6B}Z6W17-Q*G*l`?vJRttOozE|3>`S*WI=$8#4$D)7Es=mp`PpPG%| zuNo*8K6=(GCZ+UvdV!RJS9iqMk1K0irF}0X_w8XUe5KhiZ~M=WPh1~mb*QfX4|0)! zFJYfS!a7;Ayz5{C@^rFb`=)asSAonqovgT@WF<)@1Z_}uGavW z0Wu2|XG)TXf)wuTTwlIoPQrW5J1#yayFK0pny*_Se0%3!o?ROS_pSt4caAh?FK-Z# zcns?RfUE*p2{PxYKT(b8Xpoc3yACdIkdSx`Tdx5(2h^Ch6&JDPS^c?p-t!5E64xAE zKYikYhqqTOoz$1I#e<7QwV-y^mm@5hotbR{*L3bqYo4(-(<1xpn$$f9BLreiw#b+< zT-{$X(^|jEH@9=m{6~8ZJ)S3Wb;k{H#@IdCi()6TR^9aeZY#a;M5%(?E3OSI+t^>_ zKTXO1zu>)ZgkIk(g-MS3ii>t_ZrZceqF8F(&0Xb5Sqr^CPyOCuZ+%UkMd)?nnR$D5 zo?2{jAh*nS>JuGqwxn2)@1+6=M?N$;E?@gT8I)n+>oq_j2nqp^Idg-EYD|Md5tLye zp$KhE3$|}M4>AiB`RXc4O~ttu#xh;XJfTYr)e=AoITjljKfi(0=0M%440fj=N{R=C@d=PQ zoNFDYQ9>67rTy*p7iZJ9kr5YA3^T?6+%>F zI(qH<6ctbd40*lAUn2BE-L@a3KR`s{3FyW@kiG7r?1UTQG1}?-edpW-*^8|qF2}r~ z86*d5G%qB=OzrePF)u`;UhMasa}QfXoL#Ht6UbbcnJYxu2@Ylbfw=SD3(=^T$PH+a zIUw^u{#{3eIZ(H~1i1~_9FQ3xvq0u3in9}5>w)YZq_rL(GeBm6T3yh!9{Q#eL*714 zmVJG|SN>7PZdTTJN3}h_B)zfN?Na}z^XCQ7S`TK_qNJDk=50K=$1$Ii zAnaX8s+yboLNpq57b^TL4LRlwEg+|Yy!$|!otWlk49E<)ccINqcCDJvAhSTFs`#_) zcQ?1_NnPV8-?FI5;lWY4Yd!vdnkn`p$k?DqcTH`8?6akD+h^0S=wX{>elCsJmiaPFUm$z|j1;Wq?c%{zN=nStvaq2mVD_sZ8iQ%iTc z*4#d8(JrN)gBQ28v|iivvZHvn{w$Yfr~56YwMiU;%UfPo7z$obd*Z*fuZVyCq-948 zSAKJjp7~OD&lK+uN4kzQdKs;C-u%|_c+R;J`NFw%-{yB*t>3zYzom8IFUJI3`ORfE zdpFLS*`~*r)T1B$Jm{;|1jQ3ljSuPsyp1kSJG`!__;0jq&T)gxGF}PY6&Z3p3!QS_ ztq+~Ctts=*!lxl`+Ny)Uc^zlCyvjXSry z)h=^B^PK4z1J}o6ZPTXn&Dr+t!wmVkGh~lliv5*4^~gETU910?v2~=ZYG1N(^}V}} z8SRxHmfqOG6@B45S5A-yyTkQkDql=BW@<4{Kc=?Za@~}xd6JydWgnbmc<^n{rtjtc zt*;I>UwxjjQvdW_*AEi@_Z7DnK703JvE`$j8F!b|m`*qwHnEJuPNx0n*@d6pt=jJ> zsPS9Me|D4ZG3oB>RYAh9T>kp}4}KM{bXC@D#s5w2Nj8(drOZ|kFHkG}z5eC34J^3_ z_s&>xv(M?>4=47g0!w`&P41W6JaRydbNUqBoA*5&C+a0t6z+4&<63o8w(oAX(qTDw zfmLVk?kdiT)7i5{^1uYQMTVJPHBFm@qJC?fj{lq;{hj|H&-azjZ5~IwYgJ2Le7&dn z6W8x(ixRt_ZD*Y-{Hsns$T{Pa-ojGBS-xQQeCE9(iTAz+1UmgJUBu+HrT@aEYP&W! z!*IsGHEE(ZW(BWW>Q^%)Wi?`E$oqHQo*TH-GB6v3bX|&HGk;lh|q+B-5_*_L}Hx#XE#p%wO-i0GTXOqx}Rd|o>C_M zEk5;ItDaVKuiN?mpF2a^7#I$h(FMKc$X8 zs=Bx{KjvG_#@jK<*^KNuZ+n_g$ye833_8^An)rj+j_dxlX`eKM9;AT^&WCEG6u=3f z`;)K2`jc|Z8`?pIC&-+BEp~zvgOKJz!UR|W44W7PnE^6OoSB&+GNXTmz|F0ui(eT% zQ~1}zn=W>Gop1PE|K)`J#+!ffDs-xZ z-((WsXz=65(K~5UjJBH<-<)60c;=n3(p-=Ww&|1NlSB>Vvoz$GH*|tr0WwF}lb!I| z4sh!g)b2^tKwjG+$Go8*Yz}B`N54tP=AG|FPByfg>)mC)WvO;#N!XS4Y!ZEkb8aMp6NqV^Ngy?h4I2QgLyWQr@ySpj9%yN!fz~V)# zO24`$8~jt;X$En$JU*+N9;5 z3obIO?G((9@jSw{KIdhwy5_P)`wU#&uQ#@@F9^A|=a{)gwT?Espr3cd=RdQ0Ln>~~ z|L<{RNByf^ZckiyiRjF|J9}rY_(~JLh6w>Sxkf*_b-4ZpCqMQ7uJZlA&oADLj>Cyh zPs{Jq67rd~_t&k!Sj;u}Km%U{t)L_ncMeV)( z^k47v%$+o6mtV?PUlMBLrtsgU?t$L<-{rTGpKMCLxWIh>?|4DA)Oy8*A9t^-z7`jJ z^;;#&|K_4YT@^CsHlIx*7P<4Md}6I#?{6Ht^Bb?D@8&XR1@+j&y?(~WzjAdf?fL3u zVg4m~rN@0fzXkiWQXX=DZ@N|WwpKsDaq$UjL#7L+LTUAV@q4D7W=~6cw)3lO&Arzr zL|JaKFsAOww-@zuSa9*^OqD%150!Q-I{xIhafe1sgWH)O7t95IYFTKAdsmqqy2Tyk zYPaHDNvizAL%XK_eO@-{=>0$9ho#@;YDt*itC)4ojOC2uLZc|%?*BhszSMpZn=Y7m zu=o0&V}=K%Wh6wHZWgZ4n3R~8<~;G>ns+muydsMkgU(mq3}LxiI{AQ~ZZN<1qqHTD z3;spjoymA~Rr1{z$8XQ)d~?z4WKa3jlNpCOC$EStw`taPT2{U7V02yOFR%QoJx+g| zvX?$Ov$y?9i01z*eV4y4JM{j`>N)M~9Y;^kPg*Kra#8#iXU?H~M~gRUa_8&PEhS;!;qD}6{>eSCO&(C?TEXgyg^jh)?|M{n!ZmrxH{B7NTZw>8t9eZ>*-X#3U z4&49X@vKWvcOJ{0*gbn^-ZzPTuiP?3=HHz+_uRGFlPs@kANI{?%c^>zU!cd4w9x;| zwA2sBYM1W2&3p8PaaGawS)t%UP1%!hQ2;575;gYwE_i`_mIk|4%|B2!1{DPzK7@;K zNEc|qix;Ahq5xKegUkS#1uE1c3;*^ecA7q&GW*gN;rLYhxeh;9-rtwkxmC}&;C6;r zlGk#cC)Y|lyG zAQysM3NmMoAK@Y#;@gF8aNk0Ua5?4;6F_Ex zd~4df$SN?_Inm zZ}=>Y1-zkYp&v;;0gXVz&(dJms`(Fc1;{5U0qlg=a)68Qg$;0@K-Y4B%mA4M@`-%X zNukx1yN;H#y1h0~kpCT27qgLN#^EWlf6`R1#Wu|X*_9talAD(xujK&Q1F{cf&iV*; zV%BoRfZV&^ci|G`wH)kPwTxhMK*P_M`6sVFyK_z2^4l+>E()uJ%s4XtW3*Mjcf{Uh z8#;MbO)C1ZeHq8a_fj)jS)SdV;3FIRCbG)nH-Fu}r7Sb`mVBAFQNi88cFKouk4^;t z&Gho;coVjMp`(}of{kzXuPxKd*B* zeSt0c=%iaBlfFnktngYND?YV7WJcXr;XTsVwOQwK?uki!dvDgR0FBf4*BI93ycNBe zrqgW*^8L;T!l4LFdrMx3Cf-9nk3x=l!z54$fXpe4Vkc%T2PhQp!9x)`{0uS!WELpq z)f<(6sN`6ev;JLGXJpAxqT{y8%SW5-T9R6Au=Y7;g(B|~huHP5;C-%jQKYy%33PG~ zd>snNK9Id2w<|`o6SI~h=7ney7u@a8;b)K;AhSSj7fviO`yuSys_X8__k90+pKptf zUI&@49Zixum5|qRfXoKj0WznT2y-B%YmyS!ok(jrKxTl<0+}N9gUkS#1v2M- z96R_Njn#|{;KK`C8L*tCf$Sd8T8`O}?gYpTkXd4gvovOU-QpBKX(768!x>e{wzn1q z6_0E`EqVL#&05A~Oj}mmQ)uOW^yQDv>;>)DlJ&d1+&_Zi0tGV(Gcz(^IZMN2f8~@v z7j3#zAKz}D9WwXuF6pMTo(hHP=^~Q5lg#&OlRVN4J4?e@se6CWJeJ$nxr5xo=X9Sd zcp`W5Id>FCsL_laJ5v|RG5CZ{wzu52cl(TYTFLuw#QFCs3-#3oBwn1qX7V%FZ^_@B z_NXV%oG2Xl;qV62`+?DB4_mX3u6t^EKkG&An>%%rUB9I&zbaVMJmuHMWrynt`9Fx` zbqRx_!h<J z0zQ)niZ)R6fy{Z7$WF}K4Nx+V&ptVI8uHqWV_WT~fy@C7lS9{Tl>NALW}5i>j|@zD z&pyc1Fgh5qSjeVXch8zstv_+T_%wn^0CcM%*=sjmB@zylKj8E{b=rRUj3>wg)F+%z zD1$r%3Y4j-L=C2c0_6!jP@se9ATvN_f$9kHRTGY1c3A3_eSFF8!i_qG6LJ?yv(7)H z0kUI$Dk*NwWWeRtjah`-laPdv$pCjNv^{xjtNnDCTTKg1p2r>&x)-z9|GCsgW`_7U zjSp)?nDtzRPDjq09U>MVTPe{~|Nd*uk?*Sv?l)Xo{y1LPdoRc*JF`ggi52o%jT6o% zR6wo(nR6|Vuul-NmuUrFs{!@N3Fi~4U~@ocX_S6`Y`CXicD{1z<6ECTT`;)4c7L{j z$KQ-=>IvE}+P2x=d7Ztl%)!F(P}j4=cjkU`$y;FHu^Z%qdwGO?0u4*6?2}V3AWzjE z+iE`pbDJG7FS`l$5uHA8oYC6MvK+%(A)a<79|%GShPFy;_XG+%w@(EGl- z35R|@5OrCxx%9#M-g`NnrU^$cPIw~yGDdJt+j|LyN8H@=+DhG*Bwt{@FZ5wf%vF_3 z3$Lws^he8a<+b9&N&hXcyj87Sa8hl($=31|?VtUtYhEuaw_c;VykRo$<@h6e__l6- z-E;50aKw{^GyizXuhJKI^JBpdkB!`m5+8q^INR04xDOPHn#F`e5t{tI@0ZW~fV>*+ zg!2hCPzZp`X(%PCaSRH@5AaZgHjY7NfXo7gqPp9^h>i2F&04Y3znAan54LBvE*8RV zx7E^5JlKDfr8YXZV1-41Vwoez&aP5Y+@1xxT@=1H17sh_UXVFLWkfZOWB1Ev$-vzX zZ5)Hl0GS1HyYMbegRb>wt7jZFVLGW5Q!M?M*$QO7WEn~BG(k?OAhSVsfXvAy!W>9O z%`ySIQxI{M2FMJMSs-&th%g5c=UJw>+;fSDa6>T%X>A6`Eg(06!|la`|2w6Z9Q!z1 eLRlstaticKE.ecKey && ctx->staticKE.weOwnEC) FreeDer(&ctx->staticKE.ecKey); #endif + #ifdef HAVE_CURVE25519 + if (ctx->staticKE.x25519Key && ctx->staticKE.weOwnX25519) + FreeDer(&ctx->staticKE.x25519Key); + #endif #endif #ifdef WOLFSSL_STATIC_MEMORY if (ctx->heap != NULL) { @@ -6213,6 +6217,9 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) #ifndef NO_DH ssl->staticKE.weOwnDH = 0; #endif + #ifdef HAVE_CURVE25519 + ssl->staticKE.weOwnX25519 = 0; + #endif #endif #ifdef WOLFSSL_TLS13 @@ -6987,6 +6994,10 @@ void SSL_ResourceFree(WOLFSSL* ssl) if (ssl->staticKE.ecKey && ssl->staticKE.weOwnEC) FreeDer(&ssl->staticKE.ecKey); #endif + #ifdef HAVE_CURVE25519 + if (ssl->staticKE.x25519Key && ssl->staticKE.weOwnX25519) + FreeDer(&ssl->staticKE.x25519Key); + #endif #endif #ifdef WOLFSSL_STATIC_MEMORY @@ -27404,12 +27415,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, case TLS_ASYNC_BUILD: { - #if (!defined(NO_DH) && !defined(NO_RSA)) || (defined(HAVE_ECC) || \ - (defined(HAVE_CURVE25519) && defined(HAVE_ED25519)) || \ - (defined(HAVE_CURVE448) && defined(HAVE_ED448))) - word32 preSigSz, preSigIdx; - #endif - switch(ssl->specs.kea) { #ifndef NO_PSK @@ -27670,6 +27675,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, case ecc_diffie_hellman_kea: { enum wc_HashType hashType; + word32 preSigSz, preSigIdx; /* curve type, named curve, length(1) */ args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; @@ -28003,6 +28009,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, case diffie_hellman_kea: { enum wc_HashType hashType; + word32 preSigSz, preSigIdx; args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; args->length = LENGTH_SZ * 3; /* p, g, pub */ diff --git a/src/ocsp.c b/src/ocsp.c index 3b2dfce6da..b7a2d9c158 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -858,7 +858,7 @@ const char *wolfSSL_OCSP_response_status_str(long s) return "trylater"; case OCSP_SIG_REQUIRED: return "sigrequired"; - case OCSP_UNAUTHROIZED: + case OCSP_UNAUTHORIZED: return "unauthorized"; default: return "(UNKNOWN)"; diff --git a/src/sniffer.c b/src/sniffer.c index 2336f0bdc3..8a13eb71ed 100644 --- a/src/sniffer.c +++ b/src/sniffer.c @@ -91,6 +91,20 @@ #include #include #include + +#ifndef NO_RSA + #include +#endif +#ifndef NO_DH + #include +#endif +#ifdef HAVE_ECC + #include +#endif +#ifdef HAVE_CURVE25519 + #include +#endif + #ifdef NO_INLINE #include #else @@ -2146,6 +2160,9 @@ typedef struct { #ifdef HAVE_ECC DerBuffer* ecKey; #endif +#ifdef HAVE_CURVE25519 + DerBuffer* x25519Key; +#endif #if !defined(NO_RSA) && defined(WOLFSSL_STATIC_RSA) DerBuffer* rsaKey; #endif @@ -2157,14 +2174,14 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, word32 idx = 0; int ret; DerBuffer* keyBuf; -#ifdef HAVE_ECC - int useEccCurveId = ECC_CURVE_DEF; +#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) + int useCurveId = 0; #endif int devId = INVALID_DEVID; -#ifdef HAVE_ECC +#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) if (ksInfo && ksInfo->curve_id != 0) - useEccCurveId = ksInfo->curve_id; + useCurveId = ksInfo->curve_id; #endif #ifdef WOLF_CRYPTO_CB devId = CryptoDeviceId; @@ -2201,9 +2218,9 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, keys->ecKey = session->sslServer->buffers.key; /* try ECC */ #endif } - #ifdef HAVE_ECC + #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) else { - useEccCurveId = -1; /* don't try loading ECC */ + useCurveId = -1; /* don't try loading further */ } #endif } @@ -2365,7 +2382,11 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, #ifdef HAVE_ECC /* Static ECC Key */ - if (useEccCurveId >= ECC_CURVE_DEF && keys->ecKey) { + if (useCurveId >= 0 && keys->ecKey + #ifdef HAVE_CURVE25519 + && useCurveId != ECC_X25519 + #endif + ) { ecc_key key; ecc_key pubKey; int length, keyInit = 0, pubKeyInit = 0; @@ -2422,13 +2443,13 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, } /* if curve not provided in key share data, then use private key curve */ - if (useEccCurveId == ECC_CURVE_DEF && key.dp) { - useEccCurveId = key.dp->id; + if (useCurveId == 0 && key.dp) { + useCurveId = key.dp->id; } } if (ret == 0) { - ret = wc_ecc_import_x963_ex(input, length, &pubKey, useEccCurveId); + ret = wc_ecc_import_x963_ex(input, length, &pubKey, useCurveId); if (ret != 0) { SetError(ECC_PUB_DECODE_STR, error, session, FATAL_ERROR_STATE); } @@ -2466,6 +2487,82 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, } #endif /* HAVE_ECC */ +#ifdef HAVE_CURVE25519 + /* Static Curve25519 Key */ + if (useCurveId == ECC_X25519 && keys->x25519Key) { + curve25519_key key; + curve25519_key pubKey; + int length, keyInit = 0, pubKeyInit = 0; + + keyBuf = keys->x25519Key; + +#ifdef WOLFSSL_SNIFFER_KEY_CALLBACK + if (KeyCb != NULL && ksInfo) { + ret = KeyCb(session, ksInfo->named_group, + session->srvKs.key, session->srvKs.key_len, + session->cliKs.key, session->cliKs.key_len, + keyBuf, KeyCbCtx, error); + if (ret != 0) { + SetError(-1, error, session, FATAL_ERROR_STATE); + return ret; + } + } +#endif + + idx = 0; + ret = wc_curve25519_init_ex(&key, NULL, devId); + if (ret == 0) { + keyInit = 1; + ret = wc_curve25519_init(&pubKey); + } + + if (ret == 0) { + pubKeyInit = 1; + ret = wc_Curve25519PrivateKeyDecode(keyBuf->buffer, &idx, &key, + keyBuf->length); + if (ret != 0) { + SetError(ECC_DECODE_STR, error, session, FATAL_ERROR_STATE); + } + } + + if (ret == 0) { + length = CURVE25519_KEYSIZE; + if (length > *sslBytes) { + SetError(PARTIAL_INPUT_STR, error, session, FATAL_ERROR_STATE); + ret = -1; + } + } + + if (ret == 0) { + ret = wc_curve25519_import_public_ex(input, length, &pubKey, + EC25519_LITTLE_ENDIAN); + if (ret != 0) { + SetError(ECC_PUB_DECODE_STR, error, session, FATAL_ERROR_STATE); + } + } + + if (ret == 0) { + /* For Curve25519 length is always 32 */ + session->keySz = CURVE25519_KEYSIZE; + session->sslServer->arrays->preMasterSz = ENCRYPT_LEN; + + ret = wc_curve25519_shared_secret_ex(&key, &pubKey, + session->sslServer->arrays->preMasterSecret, + &session->sslServer->arrays->preMasterSz, EC25519_LITTLE_ENDIAN); + } + +#ifdef WOLFSSL_SNIFFER_STATS + if (ret != 0) + INC_STAT(SnifferStats.sslKeyFails); +#endif + + if (keyInit) + wc_curve25519_free(&key); + if (pubKeyInit) + wc_curve25519_free(&pubKey); + } +#endif /* HAVE_CURVE25519 */ + /* store for client side as well */ XMEMCPY(session->sslClient->arrays->preMasterSecret, session->sslServer->arrays->preMasterSecret, @@ -2553,6 +2650,9 @@ static int ProcessClientKeyExchange(const byte* input, int* sslBytes, #ifdef HAVE_ECC keys.ecKey = session->sslServer->staticKE.ecKey; #endif + #ifdef HAVE_CURVE25519 + keys.x25519Key = session->sslServer->staticKE.x25519Key; + #endif #endif keys.rsaKey = session->sslServer->buffers.key; return SetupKeys(input, sslBytes, session, error, NULL, &keys); @@ -2612,7 +2712,7 @@ static int ProcessKeyShare(KeyShareInfo* info, const byte* input, int len, #endif #endif /* !NO_DH */ #ifdef HAVE_ECC - #if !defined(NO_ECC256) || defined(HAVE_ALL_CURVES) + #if !defined(NO_ECC256) || defined(HAVE_ALL_CURVES) #ifndef NO_ECC_SECP case WOLFSSL_ECC_SECP256R1: info->curve_id = ECC_SECP256R1; @@ -2755,19 +2855,21 @@ static int ProcessSessionTicket(const byte* input, int* sslBytes, #ifdef WOLFSSL_TLS13 /* TLS v1.3 has hint age and nonce */ if (IsAtLeastTLSv1_3(ssl->version)) { + /* Note: Must use server session for sessions */ #ifdef HAVE_SESSION_TICKET - if (SetTicket(ssl, input, len) != 0) { + if (SetTicket(session->sslServer, input, len) != 0) { SetError(BAD_INPUT_STR, error, session, FATAL_ERROR_STATE); return -1; } + /* set haveSessionId to use the wolfSession cache */ - ssl->options.haveSessionId = 1; + session->sslServer->options.haveSessionId = 1; /* Use the wolf Session cache to retain resumption secret */ if (session->flags.cached == 0) { - WOLFSSL_SESSION* sess = GetSession(ssl, NULL, 0); + WOLFSSL_SESSION* sess = GetSession(session->sslServer, NULL, 0); if (sess == NULL) { - AddSession(ssl); /* don't re add */ + AddSession(session->sslServer); /* don't re add */ #ifdef WOLFSSL_SNIFFER_STATS INC_STAT(SnifferStats.sslResumptionInserts); #endif @@ -2858,8 +2960,10 @@ static int DoResume(SnifferSession* session, char* error) /* Resumption PSK is resumption master secret. */ session->sslServer->arrays->psk_keySz = session->sslServer->specs.hash_size; session->sslClient->arrays->psk_keySz = session->sslClient->specs.hash_size; - ret = DeriveResumptionPSK(session->sslServer, session->sslServer->session.ticketNonce.data, - session->sslServer->session.ticketNonce.len, session->sslServer->arrays->psk_key); + ret = DeriveResumptionPSK(session->sslServer, + session->sslServer->session.ticketNonce.data, + session->sslServer->session.ticketNonce.len, + session->sslServer->arrays->psk_key); /* Copy resumption PSK to client */ XMEMCPY(session->sslClient->arrays->psk_key, session->sslServer->arrays->psk_key, @@ -3181,6 +3285,9 @@ static int ProcessServerHello(int msgSz, const byte* input, int* sslBytes, #ifdef HAVE_ECC keys.ecKey = session->sslServer->staticKE.ecKey; #endif + #ifdef HAVE_CURVE25519 + keys.x25519Key = session->sslServer->staticKE.x25519Key; + #endif #endif ret = SetupKeys(session->cliKs.key, &session->cliKs.key_len, @@ -3505,6 +3612,9 @@ static int ProcessClientHello(const byte* input, int* sslBytes, return -1; } } + #ifdef HAVE_SESSION_TICKET + ssl->options.useTicket = 1; + #endif XMEMCPY(session->ticketID, input + extLen - ID_LEN, ID_LEN); } break; @@ -3657,7 +3767,7 @@ static int ProcessFinished(const byte* input, int size, int* sslBytes, #ifndef NO_SESSION_CACHE WOLFSSL_SESSION* sess = GetSession(session->sslServer, NULL, 0); if (sess == NULL) { - AddSession(session->sslServer); /* don't re add */ + AddSession(session->sslServer); /* don't re add */ #ifdef WOLFSSL_SNIFFER_STATS INC_STAT(SnifferStats.sslResumptionInserts); #endif @@ -3700,13 +3810,15 @@ static int ProcessFinished(const byte* input, int size, int* sslBytes, #ifdef HAVE_SESSION_TICKET /* derive resumption secret for next session - on finished (from client) */ - ret += DeriveResumptionSecret(session->sslClient, session->sslClient->session.masterSecret); + ret += DeriveResumptionSecret(session->sslClient, + session->sslClient->session.masterSecret); /* copy resumption secret to server */ XMEMCPY(session->sslServer->session.masterSecret, - session->sslClient->session.masterSecret, SECRET_LEN); + session->sslClient->session.masterSecret, SECRET_LEN); #ifdef SHOW_SECRETS - PrintSecret("resumption secret", session->sslClient->session.masterSecret, SECRET_LEN); + PrintSecret("resumption secret", + session->sslClient->session.masterSecret, SECRET_LEN); #endif #endif } diff --git a/src/ssl.c b/src/ssl.c index c0db9e4d55..fa55639e65 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -38619,7 +38619,7 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_bio_PrivateKey(WOLFSSL_BIO* bio, if (keyFormat == RSAk) type = EVP_PKEY_RSA; else if (keyFormat == ECDSAk) - type = EVP_PKEY_EC; + type = EVP_PKEY_EC; else if (keyFormat == DSAk) type = EVP_PKEY_DSA; else if (keyFormat == DHk) @@ -54025,10 +54025,10 @@ static int SetStaticEphemeralKey(StaticKeyExchangeInfo_t* staticKE, int keyAlgo, WOLFSSL_ENTER("SetStaticEphemeralKey"); /* if just free'ing key then skip loading */ - if (key != NULL && keySz > 0) { + if (key != NULL) { #ifndef NO_FILESYSTEM /* load file from filesystem */ - if (key && keySz == 0) { + if (key != NULL && keySz == 0) { size_t keyBufSz = 0; keyFile = (const char*)key; ret = wc_FileLoad(keyFile, &keyBuf, &keyBufSz, heap); @@ -54053,6 +54053,8 @@ static int SetStaticEphemeralKey(StaticKeyExchangeInfo_t* staticKE, int keyAlgo, if (ret == 0 && keyAlgo == WC_PK_TYPE_NONE) { if (keyFormat == ECDSAk) keyAlgo = WC_PK_TYPE_ECDH; + else if (keyFormat == X25519k) + keyAlgo = WC_PK_TYPE_CURVE25519; else keyAlgo = WC_PK_TYPE_DH; } @@ -54088,6 +54090,20 @@ static int SetStaticEphemeralKey(StaticKeyExchangeInfo_t* staticKE, int keyAlgo, } } #endif + #ifdef HAVE_CURVE25519 + if (keyAlgo == WC_PK_TYPE_NONE) { + word32 idx = 0; + curve25519_key x25519Key; + ret = wc_curve25519_init_ex(&x25519Key, heap, INVALID_DEVID); + if (ret == 0) { + ret = wc_Curve25519PrivateKeyDecode(keyBuf, &idx, &x25519Key, + keySz); + if (ret == 0) + keyAlgo = WC_PK_TYPE_CURVE25519; + wc_curve25519_free(&x25519Key); + } + } + #endif if (keyAlgo != WC_PK_TYPE_NONE) { ret = AllocDer(&der, keySz, PRIVATEKEY_TYPE, heap); @@ -54107,34 +54123,51 @@ static int SetStaticEphemeralKey(StaticKeyExchangeInfo_t* staticKE, int keyAlgo, /* if key is already allocated then set free it */ #ifndef NO_DH - if (keyAlgo == WC_PK_TYPE_DH && staticKE->dhKey && staticKE->weOwnDH) + if (keyAlgo == WC_PK_TYPE_DH && staticKE->dhKey && staticKE->weOwnDH) { FreeDer(&staticKE->dhKey); + } #endif #ifdef HAVE_ECC - if (keyAlgo == WC_PK_TYPE_ECDH && staticKE->ecKey && staticKE->weOwnEC) + if (keyAlgo == WC_PK_TYPE_ECDH && staticKE->ecKey && staticKE->weOwnEC) { FreeDer(&staticKE->ecKey); + } +#endif +#ifdef HAVE_CURVE25519 + if (keyAlgo == WC_PK_TYPE_CURVE25519 && staticKE->x25519Key && + staticKE->weOwnX25519) { + FreeDer(&staticKE->x25519Key); + } #endif switch (keyAlgo) { #ifndef NO_DH case WC_PK_TYPE_DH: - staticKE->dhKey = der; + staticKE->dhKey = der; der = NULL; staticKE->weOwnDH = 1; break; #endif #ifdef HAVE_ECC case WC_PK_TYPE_ECDH: - staticKE->ecKey = der; + staticKE->ecKey = der; der = NULL; staticKE->weOwnEC = 1; break; + #endif + #ifdef HAVE_CURVE25519 + case WC_PK_TYPE_CURVE25519: + staticKE->x25519Key = der; der = NULL; + staticKE->weOwnX25519 = 1; + break; #endif default: /* not supported */ ret = NOT_COMPILED_IN; - FreeDer(&der); break; } + if (ret != 0) { + FreeDer(&der); + } + WOLFSSL_LEAVE("SetStaticEphemeralKey", ret); return ret; @@ -54184,6 +54217,11 @@ static int GetStaticEphemeralKey(StaticKeyExchangeInfo_t* staticKE, int keyAlgo, case WC_PK_TYPE_ECDH: der = staticKE->ecKey; break; + #endif + #ifdef HAVE_CURVE25519 + case WC_PK_TYPE_CURVE25519: + der = staticKE->x25519Key; + break; #endif default: /* not supported */ diff --git a/src/tls.c b/src/tls.c index 114800e021..06c66344fd 100644 --- a/src/tls.c +++ b/src/tls.c @@ -4611,7 +4611,7 @@ int TLSX_ValidateSupportedCurves(WOLFSSL* ssl, byte first, byte second) { ephmSuite = 1; break; - #ifdef WOLFSSL_STATIC_DH + #if defined(HAVE_ECC) && defined(WOLFSSL_STATIC_DH) /* ECDH_RSA */ case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: @@ -4632,7 +4632,7 @@ int TLSX_ValidateSupportedCurves(WOLFSSL* ssl, byte first, byte second) { sig = 1; key |= ssl->pkCurveOID == pkOid; break; - #endif /* WOLFSSL_STATIC_DH */ + #endif /* HAVE_ECC && WOLFSSL_STATIC_DH */ #endif default: if (oid == ECC_X25519_OID && defOid == oid) { @@ -6858,10 +6858,23 @@ static int TLSX_KeyShare_GenX25519Key(WOLFSSL *ssl, KeyShareEntry* kse) } /* Make an Curve25519 key. */ - ret = wc_curve25519_init((curve25519_key*)kse->key); + ret = wc_curve25519_init_ex((curve25519_key*)kse->key, ssl->heap, + INVALID_DEVID); if (ret == 0) { key = (curve25519_key*)kse->key; - ret = wc_curve25519_make_key(ssl->rng, CURVE25519_KEYSIZE, key); + #ifdef WOLFSSL_STATIC_EPHEMERAL + if (ssl->staticKE.x25519Key) { + DerBuffer* keyDer = ssl->staticKE.x25519Key; + word32 idx = 0; + WOLFSSL_MSG("Using static X25519 key"); + ret = wc_Curve25519PrivateKeyDecode(keyDer->buffer, &idx, key, + keyDer->length); + } + else + #endif + { + ret = wc_curve25519_make_key(ssl->rng, CURVE25519_KEYSIZE, key); + } } } diff --git a/src/tls13.c b/src/tls13.c index 13e87ef70b..99e40a1cd3 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6547,7 +6547,7 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, #ifdef HAVE_ED25519 if (args->sigAlgo == ed25519_sa_algo && !ssl->peerEd25519KeyPresent) { - WOLFSSL_MSG("Peer sent ED22519 sig but not ED22519 cert"); + WOLFSSL_MSG("Peer sent ED25519 sig but not ED25519 cert"); ret = SIG_VERIFY_E; goto exit_dcv; } diff --git a/sslSniffer/sslSnifferTest/snifftest.c b/sslSniffer/sslSnifferTest/snifftest.c index e95d5dd688..4e0a4fe8d4 100644 --- a/sslSniffer/sslSnifferTest/snifftest.c +++ b/sslSniffer/sslSnifferTest/snifftest.c @@ -97,20 +97,27 @@ enum { #define STORE_DATA_BLOCK_SZ 1024 #endif +#if defined(HAVE_ECC) && !defined(NO_ECC_SECP) && (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) + #define DEFAULT_SERVER_EPH_KEY_ECC "../../certs/statickeys/ecc-secp256r1.pem" +#else + #define DEFAULT_SERVER_EPH_KEY_ECC "" +#endif +#ifndef NO_DH + #define DEFAULT_SERVER_EPH_KEY_DH "../../certs/statickeys/dh-ffdhe2048.pem" +#else + #define DEFAULT_SERVER_EPH_KEY_DH "" +#endif +#ifdef HAVE_CURVE25519 + #define DEFAULT_SERVER_EPH_KEY_X25519 "../../certs/statickeys/x25519.pem" +#else + #define DEFAULT_SERVER_EPH_KEY_X25519 "" +#endif -#define DEFAULT_SERVER_EPH_KEY_ECC "../../certs/statickeys/ecc-secp256r1.pem" -#define DEFAULT_SERVER_EPH_KEY_DH "../../certs/statickeys/dh-ffdhe2048.pem" #ifndef DEFAULT_SERVER_EPH_KEY - #if defined(HAVE_ECC) && !defined(NO_ECC_SECP) && \ - (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) - #if !defined(NO_DH) - #define DEFAULT_SERVER_EPH_KEY DEFAULT_SERVER_EPH_KEY_ECC "," DEFAULT_SERVER_EPH_KEY_DH - #else - #define DEFAULT_SERVER_EPH_KEY DEFAULT_SERVER_EPH_KEY_ECC - #endif - #elif !defined(NO_DH) - #define DEFAULT_SERVER_EPH_KEY DEFAULT_SERVER_EPH_KEY_DH - #endif + #define DEFAULT_SERVER_EPH_KEY \ + DEFAULT_SERVER_EPH_KEY_ECC "," \ + DEFAULT_SERVER_EPH_KEY_DH "," \ + DEFAULT_SERVER_EPH_KEY_X25519 #endif #define DEFAULT_SERVER_KEY_RSA "../../certs/server-key.pem" diff --git a/tests/api.c b/tests/api.c index 2d5404fb18..fb3a044d03 100644 --- a/tests/api.c +++ b/tests/api.c @@ -20230,10 +20230,10 @@ static int test_wc_curve25519_shared_secret_ex(void) /* * Testing wc_curve25519_make_pub */ -static int test_wc_curve25519_make_pub (void) +static int test_wc_curve25519_make_pub(void) { int ret = 0; -#if defined(HAVE_CURVE25519) +#ifdef HAVE_CURVE25519 WC_RNG rng; curve25519_key key; byte out[CURVE25519_KEYSIZE]; @@ -20248,45 +20248,45 @@ static int test_wc_curve25519_make_pub (void) } } if (ret == 0) { - ret = wc_curve25519_make_pub((int)sizeof out, out, (int)sizeof key.k.point, key.k.point); + ret = wc_curve25519_make_pub((int)sizeof(out), out, (int)sizeof(key.k), key.k); } /*test bad cases*/ if (ret == 0) { - ret = wc_curve25519_make_pub((int)sizeof key.k.point - 1, key.k.point, (int)sizeof out, out); + ret = wc_curve25519_make_pub((int)sizeof(key.k) - 1, key.k, (int)sizeof out, out); if (ret == ECC_BAD_ARG_E) { ret = 0; } } if (ret == 0) { - ret = wc_curve25519_make_pub((int)sizeof out, out, (int)sizeof key.k.point, NULL); + ret = wc_curve25519_make_pub((int)sizeof out, out, (int)sizeof(key.k), NULL); if (ret == ECC_BAD_ARG_E) { ret = 0; } } if (ret == 0) { - ret = wc_curve25519_make_pub((int)sizeof out - 1, out, (int)sizeof key.k.point, key.k.point); + ret = wc_curve25519_make_pub((int)sizeof out - 1, out, (int)sizeof(key.k), key.k); if (ret == ECC_BAD_ARG_E) { ret = 0; } } if (ret == 0) { - ret = wc_curve25519_make_pub((int)sizeof out, NULL, (int)sizeof key.k.point, key.k.point); + ret = wc_curve25519_make_pub((int)sizeof out, NULL, (int)sizeof(key.k), key.k); if (ret == ECC_BAD_ARG_E) { ret = 0; } } if (ret == 0) { /* verify clamping test */ - key.k.point[0] |= ~248; - ret = wc_curve25519_make_pub((int)sizeof out, out, (int)sizeof key.k.point, key.k.point); + key.k[0] |= ~248; + ret = wc_curve25519_make_pub((int)sizeof out, out, (int)sizeof(key.k), key.k); if (ret == ECC_BAD_ARG_E) { ret = 0; } - key.k.point[0] &= 248; + key.k[0] &= 248; } /* repeat the expected-to-succeed test. */ if (ret == 0) { - ret = wc_curve25519_make_pub((int)sizeof out, out, (int)sizeof key.k.point, key.k.point); + ret = wc_curve25519_make_pub((int)sizeof out, out, (int)sizeof(key.k), key.k); } printf(resultFmt, ret == 0 ? passed : failed); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 9ecbe0a87a..3a5688c41f 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -122,10 +122,16 @@ ASN Options: #ifdef HAVE_ED25519 #include #endif +#ifdef HAVE_CURVE25519 + #include +#endif #ifdef HAVE_ED448 #include #endif +#ifdef HAVE_CURVE448 + #include +#endif #ifdef WOLFSSL_QNX_CAAM #include @@ -554,7 +560,8 @@ static const char* TagString(byte tag) */ #define ASNIntMSBSet(asn, data, i) \ ((asn[i].tag == ASN_INTEGER) && \ - ((data[i].data.buffer.data[0] & 0x80) == 0x80)) + (data[i].data.buffer.data != NULL && \ + (data[i].data.buffer.data[0] & 0x80) == 0x80)) /* Calculate the size of a DER encoded number. @@ -1909,7 +1916,7 @@ void SetASN_ReplaceBuffer(ASNSetData *dataASN, const byte* data, word32 length) dataASN->data.buffer.length = length; } -/* Setup an ASN data item to set an muli-precision number. +/* Setup an ASN data item to set an multi-precision number. * * @param [in] dataASN Dynamic ASN data item. * @param [in] num Multi-precision number. @@ -3808,9 +3815,15 @@ static word32 SetBitString16Bit(word16 val, byte* output) #ifdef HAVE_ED25519 static const byte keyEd25519Oid[] = {43, 101, 112}; #endif /* HAVE_ED25519 */ +#ifdef HAVE_CURVE25519 + static const byte keyCurve25519Oid[] = {43, 101, 110}; +#endif #ifdef HAVE_ED448 static const byte keyEd448Oid[] = {43, 101, 113}; #endif /* HAVE_ED448 */ +#ifdef HAVE_CURVE448 + static const byte keyCurve448Oid[] = {43, 101, 111}; +#endif /* HAVE_CURVE448 */ #ifndef NO_DH static const byte keyDhOid[] = {42, 134, 72, 134, 247, 13, 1, 3, 1}; #endif /* !NO_DH */ @@ -4261,12 +4274,24 @@ const byte* OidFromId(word32 id, word32 type, word32* oidSz) *oidSz = sizeof(keyEd25519Oid); break; #endif /* HAVE_ED25519 */ + #ifdef HAVE_CURVE25519 + case X25519k: + oid = keyCurve25519Oid; + *oidSz = sizeof(keyCurve25519Oid); + break; + #endif /* HAVE_CURVE25519 */ #ifdef HAVE_ED448 case ED448k: oid = keyEd448Oid; *oidSz = sizeof(keyEd448Oid); break; #endif /* HAVE_ED448 */ + #ifdef HAVE_CURVE448 + case X448k: + oid = keyCurve448Oid; + *oidSz = sizeof(keyCurve448Oid); + break; + #endif /* HAVE_CURVE448 */ #ifndef NO_DH case DHk: oid = keyDhOid; @@ -5593,6 +5618,15 @@ int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, } break; #endif + #ifdef HAVE_CURVE25519 + case X25519k: + /* Neither NULL item nor OBJECT_ID item allowed. */ + if ((dataASN[5].tag != 0) || + (dataASN[4].tag != 0)) { + ret = ASN_PARSE_E; + } + break; + #endif #ifdef HAVE_ED448 case ED448k: /* Neither NULL item nor OBJECT_ID item allowed. */ @@ -5601,6 +5635,15 @@ int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, ret = ASN_PARSE_E; } break; + #endif + #ifdef HAVE_CURVE448 + case X448k: + /* Neither NULL item nor OBJECT_ID item allowed. */ + if ((dataASN[5].tag != 0) || + (dataASN[4].tag != 0)) { + ret = ASN_PARSE_E; + } + break; #endif /* DSAk not supported. */ /* Ignore OID lookup failures. */ @@ -12378,10 +12421,16 @@ static WC_INLINE int IsSigAlgoECC(int algoOID) #ifdef HAVE_ED25519 || (algoOID == ED25519k) #endif + #ifdef HAVE_CURVE25519 + || (algoOID == X25519k) + #endif #ifdef HAVE_ED448 || (algoOID == ED448k) #endif - ); + #ifdef HAVE_CURVE448 + || (algoOID == X448k) + #endif + ); } /* Encode an algorithm identifier. @@ -12409,7 +12458,6 @@ word32 SetAlgoID(int algoOID, byte* output, int type, int curveSz) (type == oidKeyType && algoOID == RSAk)) ? 2 : 0; algoName = OidFromId(algoOID, type, &algoSz); - if (algoName == NULL) { WOLFSSL_MSG("Unknown Algorithm"); return 0; @@ -14293,9 +14341,9 @@ static int DecodeBasicCaConstraint(const byte* input, int sz, DecodedCert* cert) ret = GetBoolean(input, &idx, sz); -/* Removed logic for WOLFSSL_X509_BASICCONS_INT which was mistreating the - * pathlen value as if it were the CA Boolean value 7/2/2021 - KH. - * When CA Boolean not asserted use the default value "False" */ + /* Removed logic for WOLFSSL_X509_BASICCONS_INT which was mistreating the + * pathlen value as if it were the CA Boolean value 7/2/2021 - KH. + * When CA Boolean not asserted use the default value "False" */ if (ret < 0) { WOLFSSL_MSG("\tfail: constraint not valid BOOLEAN, set default FALSE"); ret = 0; @@ -20212,7 +20260,7 @@ int wc_EccPublicKeyDerSize(ecc_key* key, int with_AlgCurve) #endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT */ #ifdef WOLFSSL_ASN_TEMPLATE -#if defined(HAVE_ED25519) || defined(HAVE_ED448) +#if defined(WC_ENABLE_ASYM_KEY_EXPORT) || defined(WC_ENABLE_ASYM_KEY_IMPORT) /* ASN.1 template for Ed25519 and Ed448 public key (SubkectPublicKeyInfo). * RFC 8410, 4 - Subject Public Key Fields */ @@ -20229,116 +20277,98 @@ static const ASNItem edPubKeyASN[] = { /* Number of items in ASN.1 template for Ed25519 and Ed448 public key. */ #define edPubKeyASN_Length (sizeof(edPubKeyASN) / sizeof(ASNItem)) -#endif /* HAVE_ED25519 || HAVE_ED448 */ +#endif /* WC_ENABLE_ASYM_KEY_EXPORT || WC_ENABLE_ASYM_KEY_IMPORT */ #endif /* WOLFSSL_ASN_TEMPLATE */ -#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ - (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_KEY_GEN)) +#ifdef WC_ENABLE_ASYM_KEY_EXPORT -/* Encode the public part of an Ed25519 key in DER. +/* Build ASN.1 formatted public key based on RFC 8410 * * Pass NULL for output to get the size of the encoding. * - * @param [out] output Buffer to put encoded data in. - * @param [in] key Ed25519 key object. - * @param [in] outLen Size of buffer in bytes. - * @param [in] with_header Whether to use SubjectPublicKeyInfo format. - * @return Size of encoded data in bytes on success. + * @param [in] pubKey public key buffer + * @param [in] pubKeyLen public ket buffer length + * @param [out] output Buffer to put encoded data in (optional) + * @param [in] outLen Size of buffer in bytes + * @param [in] keyType is "enum Key_Sum" like ED25519k + * @param [in] withHeader Whether to include SubjectPublicKeyInfo around key. + * @return Size of encoded data in bytes on success * @return BAD_FUNC_ARG when key is NULL. * @return MEMORY_E when dynamic memory allocation failed. */ -static int SetEd25519PublicKey(byte* output, ed25519_key* key, int outLen, - int with_header) +static int SetAsymKeyDerPublic(const byte* pubKey, word32 pubKeyLen, + byte* output, word32 outLen, int keyType, int withHeader) { + int ret = 0; #ifndef WOLFSSL_ASN_TEMPLATE - byte bitString[1 + MAX_LENGTH_SZ + 1]; - int algoSz; - int bitStringSz; - int idx; - word32 pubSz = ED25519_PUB_KEY_SIZE; -#ifdef WOLFSSL_SMALL_STACK - byte* algo = NULL; - byte* pub; + word32 idx = 0, bitStringSz, algoSz, sz = 0; #else - byte algo[MAX_ALGO_SZ]; - byte pub[ED25519_PUB_KEY_SIZE]; + int sz = 0; + DECL_ASNSETDATA(dataASN, edPubKeyASN_Length); #endif - (void)outLen; + if (pubKey == NULL) { + return BAD_FUNC_ARG; + } -#ifdef WOLFSSL_SMALL_STACK - pub = (byte*)XMALLOC(ECC_BUFSIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (pub == NULL) - return MEMORY_E; -#endif +#ifndef WOLFSSL_ASN_TEMPLATE + /* calculate size */ + if (withHeader) { + algoSz = SetAlgoID(keyType, NULL, oidKeyType, 0); + bitStringSz = SetBitString(pubKeyLen, 0, NULL); - idx = wc_ed25519_export_public(key, pub, &pubSz); - if (idx != 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return idx; + sz = algoSz + bitStringSz + pubKeyLen; + sz += SetSequence(outLen, NULL); + } + else { + sz = pubKeyLen; + } + + /* checkout output size */ + if (output != NULL && sz > outLen) { + ret = BUFFER_E; } /* headers */ - if (with_header) { -#ifdef WOLFSSL_SMALL_STACK - algo = (byte*)XMALLOC(MAX_ALGO_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (algo == NULL) { - XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return MEMORY_E; - } -#endif - algoSz = SetAlgoID(ED25519k, algo, oidKeyType, 0); - - bitStringSz = SetBitString(pubSz, 0, bitString); - - idx = SetSequence(pubSz + bitStringSz + algoSz, output); + if (ret == 0 && output != NULL && withHeader) { + /* sequence */ + idx = SetSequence(algoSz + bitStringSz + pubKeyLen, output); /* algo */ - XMEMCPY(output + idx, algo, algoSz); + algoSz = SetAlgoID(keyType, output + idx, oidKeyType, 0); idx += algoSz; /* bit string */ - XMEMCPY(output + idx, bitString, bitStringSz); + bitStringSz = SetBitString(pubKeyLen, 0, output + idx); idx += bitStringSz; } - else - idx = 0; - /* pub */ - XMEMCPY(output + idx, pub, pubSz); - idx += pubSz; + if (ret == 0 && output != NULL) { + /* pub */ + XMEMCPY(output + idx, pubKey, pubKeyLen); + idx += pubKeyLen; -#ifdef WOLFSSL_SMALL_STACK - if (with_header) { - XFREE(algo, NULL, DYNAMIC_TYPE_TMP_BUFFER); + sz = idx; } - XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return idx; + if (ret == 0) { + ret = sz; + } #else - word32 pubSz = ED25519_PUB_KEY_SIZE; - int sz = 0; - int ret = 0; - - if (with_header) { - DECL_ASNSETDATA(dataASN, edPubKeyASN_Length); - + if (withHeader) { CALLOC_ASNSETDATA(dataASN, edPubKeyASN_Length, ret, NULL); if (ret == 0) { - /* Set the Ed25519 OID. */ - SetASN_Buffer(&dataASN[2], keyEd25519Oid, sizeof(keyEd25519Oid)); + /* Set the OID. */ + SetASN_OID(&dataASN[2], keyType, oidKeyType); /* Leave space for public point. */ - SetASN_Buffer(&dataASN[3], NULL, ED25519_PUB_KEY_SIZE); - /* Calculate size of Ed25519 public key encoding. */ + SetASN_Buffer(&dataASN[3], NULL, pubKeyLen); + /* Calculate size of public key encoding. */ ret = SizeASN_Items(edPubKeyASN, dataASN, edPubKeyASN_Length, &sz); } - if ((ret == 0) && (output != NULL) && (sz > outLen)) { + if ((ret == 0) && (output != NULL) && (sz > (int)outLen)) { ret = BUFFER_E; } if ((ret == 0) && (output != NULL)) { - /* Encode Ed25519 public key. */ + /* Encode public key. */ SetASN_Items(edPubKeyASN, dataASN, edPubKeyASN_Length, output); /* Set location to encode public point. */ output = (byte*)dataASN[3].data.buffer.data; @@ -20346,25 +20376,26 @@ static int SetEd25519PublicKey(byte* output, ed25519_key* key, int outLen, FREE_ASNSETDATA(dataASN, NULL); } - else if ((output != NULL) && (ED25519_PUB_KEY_SIZE > outLen)) { + else if ((output != NULL) && (pubKeyLen > outLen)) { ret = BUFFER_E; } else if (ret == 0) { - sz = ED25519_PUB_KEY_SIZE; + sz = pubKeyLen; } if ((ret == 0) && (output != NULL)) { /* Put public key into space provided. */ - ret = wc_ed25519_export_public(key, output, &pubSz); + XMEMCPY(output, pubKey, pubKeyLen); } if (ret == 0) { ret = sz; } - - return ret; #endif /* WOLFSSL_ASN_TEMPLATE */ + return ret; } +#endif /* WC_ENABLE_ASYM_KEY_EXPORT */ +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) /* Encode the public part of an Ed25519 key in DER. * * Pass NULL for output to get the size of the encoding. @@ -20372,7 +20403,7 @@ static int SetEd25519PublicKey(byte* output, ed25519_key* key, int outLen, * @param [in] key Ed25519 key object. * @param [out] output Buffer to put encoded data in. * @param [in] outLen Size of buffer in bytes. - * @param [in] with_alg Whether to use SubjectPublicKeyInfo format. + * @param [in] withAlg Whether to use SubjectPublicKeyInfo format. * @return Size of encoded data in bytes on success. * @return BAD_FUNC_ARG when key is NULL. * @return MEMORY_E when dynamic memory allocation failed. @@ -20380,175 +20411,24 @@ static int SetEd25519PublicKey(byte* output, ed25519_key* key, int outLen, int wc_Ed25519PublicKeyToDer(ed25519_key* key, byte* output, word32 inLen, int withAlg) { -#ifndef WOLFSSL_ASN_TEMPLATE - word32 infoSz = 0; - word32 keySz = 0; - int ret; + int ret; + byte pubKey[ED25519_PUB_KEY_SIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); - if (output == NULL || key == NULL) { + if (key == NULL || output == NULL) { return BAD_FUNC_ARG; } - if (withAlg) { - /* buffer space for algorithm */ - infoSz += MAX_SEQ_SZ; - infoSz += MAX_ALGO_SZ; - - /* buffer space for public key sequence */ - infoSz += MAX_SEQ_SZ; - infoSz += TRAILING_ZERO; - } - - if ((ret = wc_ed25519_export_public(key, output, &keySz)) != BUFFER_E) { - WOLFSSL_MSG("Error in getting ECC public key size"); - return ret; - } - - if (inLen < keySz + infoSz) { - return BUFFER_E; - } -#else - if (output == NULL || key == NULL) { - return BAD_FUNC_ARG; - } -#endif - - return SetEd25519PublicKey(output, key, inLen, withAlg); -} -#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT && (WOLFSSL_CERT_GEN || WOLFSSL_KEY_GEN) */ -#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) && \ - (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_KEY_GEN)) - -/* Encode the public part of an Ed448 key in DER. - * - * Pass NULL for output to get the size of the encoding. - * - * @param [out] output Buffer to put encoded data in. - * @param [in] key Ed448 key object. - * @param [in] outLen Size of buffer in bytes. - * @param [in] with_header Whether to use SubjectPublicKeyInfo format. - * @return Size of encoded data in bytes on success. - * @return BAD_FUNC_ARG when key is NULL. - * @return MEMORY_E when dynamic memory allocation failed. - */ -static int SetEd448PublicKey(byte* output, ed448_key* key, int outLen, - int with_header) -{ -#ifndef WOLFSSL_ASN_TEMPLATE - byte bitString[1 + MAX_LENGTH_SZ + 1]; - int algoSz; - int bitStringSz; - int idx; - word32 pubSz = ED448_PUB_KEY_SIZE; -#ifdef WOLFSSL_SMALL_STACK - byte* algo = NULL; - byte* pub = NULL; -#else - byte algo[MAX_ALGO_SZ]; - byte pub[ED448_PUB_KEY_SIZE]; -#endif - - (void)outLen; - -#ifdef WOLFSSL_SMALL_STACK - pub = (byte*)XMALLOC(ECC_BUFSIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (pub == NULL) - return MEMORY_E; -#endif - - idx = wc_ed448_export_public(key, pub, &pubSz); - if (idx != 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return idx; - } - - /* headers */ - if (with_header) { -#ifdef WOLFSSL_SMALL_STACK - algo = (byte*)XMALLOC(MAX_ALGO_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (algo == NULL) { - XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return MEMORY_E; - } -#endif - algoSz = SetAlgoID(ED448k, algo, oidKeyType, 0); - - bitStringSz = SetBitString(pubSz, 0, bitString); - - idx = SetSequence(pubSz + bitStringSz + algoSz, output); - /* algo */ - XMEMCPY(output + idx, algo, algoSz); - idx += algoSz; - /* bit string */ - XMEMCPY(output + idx, bitString, bitStringSz); - idx += bitStringSz; - } - else - idx = 0; - - /* pub */ - XMEMCPY(output + idx, pub, pubSz); - idx += pubSz; - -#ifdef WOLFSSL_SMALL_STACK - if (with_header) { - XFREE(algo, NULL, DYNAMIC_TYPE_TMP_BUFFER); - } - XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - - return idx; -#else - word32 pubSz = ED448_PUB_KEY_SIZE; - int sz = 0; - int ret = 0; - - if (with_header) { - DECL_ASNSETDATA(dataASN, edPubKeyASN_Length); - - CALLOC_ASNSETDATA(dataASN, edPubKeyASN_Length, ret, NULL); - - if (ret == 0) { - /* Set the Ed448 OID. */ - SetASN_Buffer(&dataASN[2], keyEd448Oid, sizeof(keyEd448Oid)); - /* Leave space for public point. */ - SetASN_Buffer(&dataASN[3], NULL, ED448_PUB_KEY_SIZE); - /* Calculate size of Ed448 public key encoding. */ - ret = SizeASN_Items(edPubKeyASN, dataASN, edPubKeyASN_Length, &sz); - } - if ((ret == 0) && (output != NULL) && (sz > outLen)) { - ret = BUFFER_E; - } - if ((ret == 0) && (output != NULL)) { - /* Encode Ed448 public key. */ - SetASN_Items(edPubKeyASN, dataASN, edPubKeyASN_Length, output); - /* Set location to encode public point. */ - output = (byte*)dataASN[3].data.buffer.data; - } - - FREE_ASNSETDATA(dataASN, NULL); - } - else if ((output != NULL) && (ED448_PUB_KEY_SIZE > outLen)) { - ret = BUFFER_E; - } - else if (ret == 0) { - sz = ED448_PUB_KEY_SIZE; - } - - if ((ret == 0) && (output != NULL)) { - /* Put public key into space provided. */ - ret = wc_ed448_export_public(key, output, &pubSz); - } + ret = wc_ed25519_export_public(key, pubKey, &pubKeyLen); if (ret == 0) { - ret = sz; + ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, inLen, + ED25519k, withAlg); } - return ret; -#endif /* WOLFSSL_ASN_TEMPLATE */ } +#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT */ +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) /* Encode the public part of an Ed448 key in DER. * * Pass NULL for output to get the size of the encoding. @@ -20556,7 +20436,7 @@ static int SetEd448PublicKey(byte* output, ed448_key* key, int outLen, * @param [in] key Ed448 key object. * @param [out] output Buffer to put encoded data in. * @param [in] outLen Size of buffer in bytes. - * @param [in] with_alg Whether to use SubjectPublicKeyInfo format. + * @param [in] withAlg Whether to use SubjectPublicKeyInfo format. * @return Size of encoded data in bytes on success. * @return BAD_FUNC_ARG when key is NULL. * @return MEMORY_E when dynamic memory allocation failed. @@ -20564,42 +20444,22 @@ static int SetEd448PublicKey(byte* output, ed448_key* key, int outLen, int wc_Ed448PublicKeyToDer(ed448_key* key, byte* output, word32 inLen, int withAlg) { -#ifndef WOLFSSL_ASN_TEMPLATE - word32 infoSz = 0; - word32 keySz = 0; - int ret; + int ret; + byte pubKey[ED448_PUB_KEY_SIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); - if (output == NULL || key == NULL) { + if (key == NULL || output == NULL) { return BAD_FUNC_ARG; } - if (withAlg) { - /* buffer space for algorithm */ - infoSz += MAX_SEQ_SZ; - infoSz += MAX_ALGO_SZ; - - /* buffer space for public key sequence */ - infoSz += MAX_SEQ_SZ; - infoSz += TRAILING_ZERO; + ret = wc_ed448_export_public(key, pubKey, &pubKeyLen); + if (ret == 0) { + ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, inLen, + ED448k, withAlg); } - - if ((ret = wc_ed448_export_public(key, output, &keySz)) != BUFFER_E) { - WOLFSSL_MSG("Error in getting ECC public key size"); - return ret; - } - - if (inLen < keySz + infoSz) { - return BUFFER_E; - } -#else - if (output == NULL || key == NULL) { - return BAD_FUNC_ARG; - } -#endif - - return SetEd448PublicKey(output, key, inLen, withAlg); + return ret; } -#endif /* HAVE_ED448 && HAVE_ED448_KEY_EXPORT && (WOLFSSL_CERT_GEN || WOLFSSL_KEY_GEN) */ +#endif /* HAVE_ED448 && HAVE_ED448_KEY_EXPORT */ #ifdef WOLFSSL_CERT_GEN @@ -22016,7 +21876,7 @@ static int EncodePublicKey(int keyType, byte* output, int outLen, #endif /* HAVE_ECC */ #ifdef HAVE_ED25519 case ED25519_KEY: - ret = SetEd25519PublicKey(output, ed25519Key, outLen, 1); + ret = wc_Ed25519PublicKeyToDer(ed25519Key, output, outLen, 1); if (ret <= 0) { ret = PUBLIC_KEY_E; } @@ -22024,7 +21884,7 @@ static int EncodePublicKey(int keyType, byte* output, int outLen, #endif #ifdef HAVE_ED448 case ED448_KEY: - ret = SetEd448PublicKey(output, ed448Key, outLen, 1); + ret = wc_Ed448PublicKeyToDer(ed448Key, output, outLen, 1); if (ret <= 0) { ret = PUBLIC_KEY_E; } @@ -22476,8 +22336,8 @@ static int EncodeCert(Cert* cert, DerCert* der, RsaKey* rsaKey, ecc_key* eccKey, if (cert->keyType == ED25519_KEY) { if (ed25519Key == NULL) return PUBLIC_KEY_E; - der->publicKeySz = SetEd25519PublicKey(der->publicKey, ed25519Key, - sizeof(der->publicKey), 1); + der->publicKeySz = wc_Ed25519PublicKeyToDer(ed25519Key, der->publicKey, + (word32)sizeof(der->publicKey), 1); } #endif @@ -22485,8 +22345,8 @@ static int EncodeCert(Cert* cert, DerCert* der, RsaKey* rsaKey, ecc_key* eccKey, if (cert->keyType == ED448_KEY) { if (ed448Key == NULL) return PUBLIC_KEY_E; - der->publicKeySz = SetEd448PublicKey(der->publicKey, ed448Key, - sizeof(der->publicKey), 1); + der->publicKeySz = wc_Ed448PublicKeyToDer(ed448Key, der->publicKey, + (word32)sizeof(der->publicKey), 1); } #endif @@ -23575,8 +23435,8 @@ static int EncodeCertReq(Cert* cert, DerCert* der, RsaKey* rsaKey, if (cert->keyType == ED25519_KEY) { if (ed25519Key == NULL) return PUBLIC_KEY_E; - der->publicKeySz = SetEd25519PublicKey(der->publicKey, ed25519Key, - sizeof(der->publicKey), 1); + der->publicKeySz = wc_Ed25519PublicKeyToDer(ed25519Key, der->publicKey, + (word32)sizeof(der->publicKey), 1); } #endif @@ -23584,8 +23444,8 @@ static int EncodeCertReq(Cert* cert, DerCert* der, RsaKey* rsaKey, if (cert->keyType == ED448_KEY) { if (ed448Key == NULL) return PUBLIC_KEY_E; - der->publicKeySz = SetEd448PublicKey(der->publicKey, ed448Key, - sizeof(der->publicKey), 1); + der->publicKeySz = wc_Ed448PublicKeyToDer(ed448Key, der->publicKey, + (word32)sizeof(der->publicKey), 1); } #endif if (der->publicKeySz <= 0) @@ -24226,13 +24086,15 @@ static int SetKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey, #endif #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) /* ED25519 public key */ - if (ed25519Key != NULL) - bufferSz = SetEd25519PublicKey(buf, ed25519Key, MAX_PUBLIC_KEY_SZ, 0); + if (ed25519Key != NULL) { + bufferSz = wc_Ed25519PublicKeyToDer(ed25519Key, buf, MAX_PUBLIC_KEY_SZ, 0); + } #endif #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) /* ED448 public key */ - if (ed448Key != NULL) - bufferSz = SetEd448PublicKey(buf, ed448Key, MAX_PUBLIC_KEY_SZ, 0); + if (ed448Key != NULL) { + bufferSz = wc_Ed448PublicKeyToDer(ed448Key, buf, MAX_PUBLIC_KEY_SZ, 0); + } #endif if (bufferSz <= 0) { @@ -26870,8 +26732,8 @@ int wc_EccKeyToPKCS8(ecc_key* key, byte* output, #endif /* HAVE_ECC_KEY_EXPORT && !NO_ASN_CRYPT */ #endif /* HAVE_ECC */ +#ifdef WC_ENABLE_ASYM_KEY_IMPORT #ifdef WOLFSSL_ASN_TEMPLATE -#if defined(HAVE_ED25519) || defined(HAVE_ED448) /* ASN.1 template for Ed25519 and Ed448 private key. * RFC 8410, 7 - Private Key Format (but public value is EXPLICIT OCTET_STRING) */ @@ -26896,25 +26758,28 @@ static const ASNItem edKeyASN[] = { /* Number of items in ASN.1 template for Ed25519 and Ed448 private key. */ #define edKeyASN_Length (sizeof(edKeyASN) / sizeof(ASNItem)) -#endif /* HAVE_ED25519 || HAVE_ED448 */ -#endif /* WOLFSSL_ASN_TEMPLATE */ +#endif -#ifdef HAVE_ED25519 - -#ifdef HAVE_ED25519_KEY_IMPORT - -int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, - ed25519_key* key, word32 inSz) +static int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz, + byte* privKey, word32* privKeyLen, + byte* pubKey, word32* pubKeyLen, int keyType) { + int ret = 0; #ifndef WOLFSSL_ASN_TEMPLATE - word32 oid; - int ret, version, length, endKeyIdx, privSz, pubSz; + word32 oid; + int version, length, endKeyIdx, privSz, pubSz; const byte* priv; const byte* pub; +#else + DECL_ASNGETDATA(dataASN, edKeyASN_Length); +#endif - if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) + if (input == NULL || inOutIdx == NULL || inSz == 0 || + privKey == NULL || privKeyLen == NULL) { return BAD_FUNC_ARG; + } +#ifndef WOLFSSL_ASN_TEMPLATE if (GetSequence(input, inOutIdx, &length, inSz) >= 0) { endKeyIdx = *inOutIdx + length; @@ -26927,7 +26792,7 @@ int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, if (GetAlgoId(input, inOutIdx, &oid, oidKeyType, inSz) < 0) return ASN_PARSE_E; - if (oid != ED25519k) + if (oid != (word32)keyType) return ASN_PARSE_E; if (GetOctetString(input, inOutIdx, &length, inSz) < 0) @@ -26943,42 +26808,45 @@ int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, if (GetOctetString(input, inOutIdx, &privSz, inSz) < 0) return ASN_PARSE_E; - if (privSz != 32) - return ASN_PARSE_E; - priv = input + *inOutIdx; *inOutIdx += privSz; endKeyIdx = *inOutIdx; } if (endKeyIdx == (int)*inOutIdx) { - ret = wc_ed25519_import_private_only(priv, privSz, key); + *privKeyLen = privSz; + XMEMCPY(privKey, priv, *privKeyLen); + if (pubKeyLen != NULL) + *pubKeyLen = 0; } else { if (GetASNHeader(input, ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1, inOutIdx, &length, inSz) < 0) { return ASN_PARSE_E; } - if (GetOctetString(input, inOutIdx, &pubSz, inSz) < 0) + if (GetOctetString(input, inOutIdx, &pubSz, inSz) < 0) { return ASN_PARSE_E; + } pub = input + *inOutIdx; *inOutIdx += pubSz; - ret = wc_ed25519_import_private_key(priv, privSz, pub, pubSz, key); + *privKeyLen = privSz; + XMEMCPY(privKey, priv, *privKeyLen); + if (pubKeyLen != NULL) + *pubKeyLen = pubSz; + if (pubKey != NULL && pubKeyLen != NULL) + XMEMCPY(pubKey, pub, *pubKeyLen); } if (ret == 0 && endKeyIdx != (int)*inOutIdx) return ASN_PARSE_E; - - return ret; #else - DECL_ASNGETDATA(dataASN, edKeyASN_Length); - int ret = 0; - CALLOC_ASNGETDATA(dataASN, edKeyASN_Length, ret, NULL); if (ret == 0) { - /* Require Ed25519 OID. */ - GetASN_ExpBuffer(&dataASN[3], keyEd25519Oid, sizeof(keyEd25519Oid)); + /* Require OID. */ + word32 oidSz; + const byte* oid = OidFromId(keyType, oidKeyType, &oidSz); + GetASN_ExpBuffer(&dataASN[3], oid, oidSz); /* Parse full private key. */ ret = GetASN_Items(edKeyASN, dataASN, edKeyASN_Length, 1, input, inOutIdx, inSz); @@ -26992,50 +26860,61 @@ int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, } } /* Check the private value length is correct. */ - if ((ret == 0) && dataASN[5].data.ref.length != ED25519_KEY_SIZE) { + if ((ret == 0) && dataASN[5].data.ref.length > *privKeyLen) { ret = ASN_PARSE_E; } if ((ret == 0) && dataASN[7].tag == 0) { - /* Import just the private value. */ - ret = wc_ed25519_import_private_only(dataASN[5].data.ref.data, - ED25519_KEY_SIZE, key); + *privKeyLen = dataASN[5].data.ref.length; + XMEMCPY(privKey, dataASN[5].data.ref.data, *privKeyLen); + if (pubKeyLen != NULL) + *pubKeyLen = 0; } else if ((ret == 0) && - (dataASN[8].data.ref.length != ED25519_PUB_KEY_SIZE)) { + (dataASN[8].data.ref.length > *pubKeyLen)) { ret = ASN_PARSE_E; } else if (ret == 0) { /* Import private and public value. */ - ret = wc_ed25519_import_private_key(dataASN[5].data.ref.data, - ED25519_KEY_SIZE, dataASN[8].data.ref.data, - ED25519_PUB_KEY_SIZE, key); + *privKeyLen = dataASN[5].data.ref.length; + XMEMCPY(privKey, dataASN[5].data.ref.data, *privKeyLen); + if (pubKeyLen != NULL) + *pubKeyLen = dataASN[8].data.ref.length; + if (pubKey != NULL && pubKeyLen != NULL) + XMEMCPY(pubKey, dataASN[8].data.ref.data, *pubKeyLen); } FREE_ASNGETDATA(dataASN, NULL); - return ret; #endif /* WOLFSSL_ASN_TEMPLATE */ + return ret; } - -int wc_Ed25519PublicKeyDecode(const byte* input, word32* inOutIdx, - ed25519_key* key, word32 inSz) +static int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx, word32 inSz, + byte* pubKey, word32* pubKeyLen, int keyType) { + int ret = 0; #ifndef WOLFSSL_ASN_TEMPLATE - int length; - int ret; + int length; + word32 oid; +#else + DECL_ASNGETDATA(dataASN, edPubKeyASN_Length); +#endif - if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) + if (input == NULL || inSz == 0 || inOutIdx == NULL || + pubKey == NULL || pubKeyLen == NULL) { return BAD_FUNC_ARG; + } +#ifndef WOLFSSL_ASN_TEMPLATE if (GetSequence(input, inOutIdx, &length, inSz) < 0) return ASN_PARSE_E; if (GetSequence(input, inOutIdx, &length, inSz) < 0) return ASN_PARSE_E; - ret = SkipObjectId(input, inOutIdx, inSz); - if (ret != 0) - return ret; + if (GetObjectId(input, inOutIdx, &oid, oidKeyType, inSz) < 0) + return ASN_PARSE_E; + if (oid != (word32)keyType) + return ASN_PARSE_E; /* key header */ ret = CheckBitString(input, inOutIdx, NULL, inSz, 1, NULL); @@ -27043,19 +26922,16 @@ int wc_Ed25519PublicKeyDecode(const byte* input, word32* inOutIdx, return ret; /* This is the raw point data compressed or uncompressed. */ - if (wc_ed25519_import_public(input + *inOutIdx, inSz - *inOutIdx, key) != 0) - return ASN_ECC_KEY_E; - - return 0; + *pubKeyLen = inSz - *inOutIdx; + XMEMCPY(pubKey, input + *inOutIdx, *pubKeyLen); #else - DECL_ASNGETDATA(dataASN, edPubKeyASN_Length); - int ret = 0; - CALLOC_ASNGETDATA(dataASN, edPubKeyASN_Length, ret, NULL); if (ret == 0) { - /* Require Ed25519 OID. */ - GetASN_ExpBuffer(&dataASN[2], keyEd25519Oid, sizeof(keyEd25519Oid)); + /* Require OID. */ + word32 oidSz; + const byte* oid = OidFromId(keyType, oidKeyType, &oidSz); + GetASN_ExpBuffer(&dataASN[2], oid, oidSz); /* Decode Ed25519 private key. */ ret = GetASN_Items(edPubKeyASN, dataASN, edPubKeyASN_Length, 1, input, inOutIdx, inSz); @@ -27064,103 +26940,201 @@ int wc_Ed25519PublicKeyDecode(const byte* input, word32* inOutIdx, } } /* Check the public value length is correct. */ - if ((ret == 0) && (dataASN[3].data.ref.length != ED25519_PUB_KEY_SIZE)) { + if ((ret == 0) && (dataASN[3].data.ref.length > *pubKeyLen)) { ret = ASN_PARSE_E; } if (ret == 0) { - /* Import the public value. */ - ret = wc_ed25519_import_public(dataASN[3].data.ref.data, - ED25519_PUB_KEY_SIZE, key); - if (ret != 0) { - ret = ASN_ECC_KEY_E; - } + *pubKeyLen = dataASN[3].data.ref.length; + XMEMCPY(pubKey, dataASN[3].data.ref.data, *pubKeyLen); } FREE_ASNGETDATA(dataASN, NULL); - return ret; #endif /* WOLFSSL_ASN_TEMPLATE */ + return ret; +} +#endif /* WC_ENABLE_ASYM_KEY_IMPORT */ + +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) +int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, + ed25519_key* key, word32 inSz) +{ + int ret; + byte privKey[ED25519_KEY_SIZE], pubKey[ED25519_PUB_KEY_SIZE]; + word32 privKeyLen = (word32)sizeof(privKey); + word32 pubKeyLen = (word32)sizeof(pubKey); + + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { + return BAD_FUNC_ARG; + } + + ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen, + pubKey, &pubKeyLen, ED25519k); + if (ret == 0) { + if (pubKeyLen == 0) { + ret = wc_ed25519_import_private_only(privKey, privKeyLen, key); + } + else { + ret = wc_ed25519_import_private_key(privKey, privKeyLen, + pubKey, pubKeyLen, key); + } + } + return ret; } -#endif /* HAVE_ED25519_KEY_IMPORT */ - -#if defined(WOLFSSL_KEY_GEN) && defined(HAVE_ED25519_KEY_EXPORT) - -/* build DER formatted ED25519 key, - * return length on success, negative on error */ -static int wc_BuildEd25519KeyDer(ed25519_key* key, byte* output, word32 inLen, - int pubOut) +int wc_Ed25519PublicKeyDecode(const byte* input, word32* inOutIdx, + ed25519_key* key, word32 inSz) { -#ifndef WOLFSSL_ASN_TEMPLATE - byte algoArray[MAX_ALGO_SZ]; - byte ver[MAX_VERSION_SZ]; - byte seq[MAX_SEQ_SZ]; - int ret; - word32 idx = 0, seqSz, verSz, algoSz, privSz, pubSz = 0; + int ret; + byte pubKey[ED25519_PUB_KEY_SIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); - if (key == NULL || output == NULL || inLen == 0) + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { return BAD_FUNC_ARG; - - if (pubOut) - pubSz = 2 + 2 + ED25519_PUB_KEY_SIZE; - privSz = 2 + 2 + ED25519_KEY_SIZE; - algoSz = SetAlgoID(ED25519k, algoArray, oidKeyType, 0); - verSz = SetMyVersion(0, ver, FALSE); - seqSz = SetSequence(verSz + algoSz + privSz + pubSz, seq); - - if (seqSz + verSz + algoSz + privSz + pubSz > inLen) - return BAD_FUNC_ARG; - - /* write out */ - /* seq */ - XMEMCPY(output + idx, seq, seqSz); - idx = seqSz; - /* ver */ - XMEMCPY(output + idx, ver, verSz); - idx += verSz; - /* algo */ - XMEMCPY(output + idx, algoArray, algoSz); - idx += algoSz; - /* privKey */ - idx += SetOctetString(2 + ED25519_KEY_SIZE, output + idx); - idx += SetOctetString(ED25519_KEY_SIZE, output + idx); - ret = wc_ed25519_export_private_only(key, output + idx, &privSz); - if (ret != 0) - return ret; - idx += privSz; - /* pubKey */ - if (pubOut) { - idx += SetExplicit(1, 2 + ED25519_PUB_KEY_SIZE, output + idx); - idx += SetOctetString(ED25519_KEY_SIZE, output + idx); - ret = wc_ed25519_export_public(key, output + idx, &pubSz); - if (ret != 0) - return ret; - idx += pubSz; } - return idx; + ret = DecodeAsymKeyPublic(input, inOutIdx, inSz, + pubKey, &pubKeyLen, ED25519k); + if (ret == 0) { + ret = wc_ed25519_import_public(pubKey, pubKeyLen, key); + } + return ret; +} +#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_IMPORT */ + +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT) +int wc_Curve25519PrivateKeyDecode(const byte* input, word32* inOutIdx, + curve25519_key* key, word32 inSz) +{ + int ret; + byte privKey[CURVE25519_KEYSIZE]; + word32 privKeyLen = CURVE25519_KEYSIZE; + + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { + return BAD_FUNC_ARG; + } + + ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen, + NULL, NULL, X25519k); + if (ret == 0) { + ret = wc_curve25519_import_private(privKey, privKeyLen, key); + } + return ret; +} + +int wc_Curve25519PublicKeyDecode(const byte* input, word32* inOutIdx, + curve25519_key* key, word32 inSz) +{ + int ret; + byte pubKey[CURVE25519_KEYSIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); + + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { + return BAD_FUNC_ARG; + } + + ret = DecodeAsymKeyPublic(input, inOutIdx, inSz, + pubKey, &pubKeyLen, X25519k); + if (ret == 0) { + ret = wc_curve25519_import_public(pubKey, pubKeyLen, key); + } + return ret; +} +#endif /* HAVE_CURVE25519 && HAVE_ED25519_KEY_IMPORT */ + + +#ifdef WC_ENABLE_ASYM_KEY_EXPORT + +/* Build ASN.1 formatted key based on RFC 5958 (Asymmetric Key Packages) + * + * Pass NULL for output to get the size of the encoding. + * + * @param [in] privKey private key buffer + * @param [in] privKeyLen private ket buffer length + * @param [in] pubKey public key buffer (optional) + * @param [in] pubKeyLen public ket buffer length + * @param [out] output Buffer to put encoded data in (optional) + * @param [in] outLen Size of buffer in bytes + * @param [in] keyType is "enum Key_Sum" like ED25519k + * @return Size of encoded data in bytes on success + * @return BAD_FUNC_ARG when key is NULL. + * @return MEMORY_E when dynamic memory allocation failed. + * @return LENGTH_ONLY_E return length only. + */ +static int SetAsymKeyDer(const byte* privKey, word32 privKeyLen, + const byte* pubKey, word32 pubKeyLen, + byte* output, word32 outLen, int keyType) +{ + int ret = 0; +#ifndef WOLFSSL_ASN_TEMPLATE + word32 idx = 0, seqSz, verSz, algoSz, privSz, pubSz = 0, sz; #else DECL_ASNSETDATA(dataASN, edKeyASN_Length); - word32 privSz; - int ret = 0; int sz; +#endif /* Validate parameters. */ - if ((key == NULL) || (output == NULL) || (inLen == 0)) { + if (privKey == NULL || outLen == 0) { + return BAD_FUNC_ARG; + } + +#ifndef WOLFSSL_ASN_TEMPLATE + /* calculate size */ + if (pubKey) { + pubSz = 2 + 2 + pubKeyLen; + } + privSz = 2 + 2 + privKeyLen; + algoSz = SetAlgoID(keyType, NULL, oidKeyType, 0); + verSz = 3; /* version is 3 bytes (enum + id + version(byte)) */ + seqSz = SetSequence(verSz + algoSz + privSz + pubSz, NULL); + sz = seqSz + verSz + algoSz + privSz + pubSz; + + /* checkout output size */ + if (ret == 0 && output != NULL && sz > outLen) { ret = BAD_FUNC_ARG; } + if (ret == 0 && output != NULL) { + /* write out */ + /* seq */ + seqSz = SetSequence(verSz + algoSz + privSz + pubSz, output); + idx = seqSz; + /* ver */ + SetMyVersion(0, output + idx, FALSE); + idx += verSz; + /* algo */ + algoSz = SetAlgoID(keyType, output + idx, oidKeyType, 0); + idx += algoSz; + /* privKey */ + idx += SetOctetString(2 + privKeyLen, output + idx); + idx += SetOctetString(privKeyLen, output + idx); + XMEMCPY(output + idx, privKey, privKeyLen); + idx += privKeyLen; + /* pubKey */ + if (pubKey) { + idx += SetExplicit(1, 2 + pubKeyLen, output + idx); + idx += SetOctetString(pubKeyLen, output + idx); + XMEMCPY(output + idx, pubKey, pubKeyLen); + idx += pubKeyLen; + } + + ret = idx; + } +#else + CALLOC_ASNSETDATA(dataASN, edKeyASN_Length, ret, NULL); if (ret == 0) { - /* Set Ed25519 OID. */ - SetASN_Buffer(&dataASN[1], keyEd25519Oid, sizeof(keyEd25519Oid)); + /* Set version = 0 */ + SetASN_Int8Bit(&dataASN[1], 0); + /* Set OID. */ + SetASN_OID(&dataASN[3], keyType, oidKeyType); /* Leave space for private key. */ - SetASN_Buffer(&dataASN[5], NULL, ED25519_KEY_SIZE); + SetASN_Buffer(&dataASN[5], NULL, privKeyLen); /* Don't write out attributes. */ dataASN[6].noOut = 1; - if (pubOut) { + if (pubKey) { /* Leave space for public key. */ - SetASN_Buffer(&dataASN[8], NULL, ED25519_PUB_KEY_SIZE); + SetASN_Buffer(&dataASN[8], NULL, pubKeyLen); } else { /* Don't put out public part. */ @@ -27170,24 +27144,22 @@ static int wc_BuildEd25519KeyDer(ed25519_key* key, byte* output, word32 inLen, /* Calculate the size of encoding. */ ret = SizeASN_Items(edKeyASN, dataASN, edKeyASN_Length, &sz); } + /* Check buffer is big enough. */ - if ((ret == 0) && (sz > (int)inLen)) { + if ((ret == 0) && (output != NULL) && (sz > (int)outLen)) { ret = BAD_FUNC_ARG; } - if (ret == 0) { - /* Encode Ed25519 private key. */ + if (ret == 0 && output != NULL) { + /* Encode private key. */ SetASN_Items(edKeyASN, dataASN, edKeyASN_Length, output); /* Put private value into space provided. */ - privSz = ED25519_KEY_SIZE; - ret = wc_ed25519_export_private_only(key, - (byte*)dataASN[5].data.buffer.data, &privSz); - } - if ((ret == 0) && pubOut) { - /* Put public value into space provided. */ - word32 pubSz = ED25519_PUB_KEY_SIZE; - ret = wc_ed25519_export_public(key, (byte*)dataASN[8].data.buffer.data, - &pubSz); + XMEMCPY((byte*)dataASN[5].data.buffer.data, privKey, privKeyLen); + + if (pubKey != NULL) { + /* Put public value into space provided. */ + XMEMCPY((byte*)dataASN[8].data.buffer.data, pubKey, pubKeyLen); + } } if (ret == 0) { @@ -27196,348 +27168,234 @@ static int wc_BuildEd25519KeyDer(ed25519_key* key, byte* output, word32 inLen, } FREE_ASNSETDATA(dataASN, NULL); - return ret; #endif + return ret; } +#endif /* WC_ENABLE_ASYM_KEY_EXPORT */ -/* Write a Private ecc key, including public to DER format, +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) +/* Write a Private ED25519 key, including public to DER format, * length on success else < 0 */ int wc_Ed25519KeyToDer(ed25519_key* key, byte* output, word32 inLen) { - return wc_BuildEd25519KeyDer(key, output, inLen, 1); + if (key == NULL) { + return BAD_FUNC_ARG; + } + return SetAsymKeyDer(key->k, ED25519_KEY_SIZE, + key->p, ED25519_PUB_KEY_SIZE, output, inLen, ED25519k); } -/* Write only private Ed25519 key to DER format, +/* Write only private ED25519 key to DER format, * length on success else < 0 */ int wc_Ed25519PrivateKeyToDer(ed25519_key* key, byte* output, word32 inLen) { - return wc_BuildEd25519KeyDer(key, output, inLen, 0); + if (key == NULL) { + return BAD_FUNC_ARG; + } + return SetAsymKeyDer(key->k, ED25519_KEY_SIZE, + NULL, 0, output, inLen, ED25519k); +} +#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT */ + +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) +/* Write only private Curve25519 key to DER format, + * length on success else < 0 */ +int wc_Curve25519PrivateKeyToDer(curve25519_key* key, byte* output, word32 inLen) +{ + int ret; + byte privKey[CURVE25519_KEYSIZE]; + word32 privKeyLen = CURVE25519_KEYSIZE; + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_curve25519_export_private_raw(key, privKey, &privKeyLen); + if (ret == 0) { + ret = SetAsymKeyDer(privKey, privKeyLen, NULL, 0, output, inLen, + X25519k); + } + return ret; } -#endif /* WOLFSSL_KEY_GEN && HAVE_ED25519_KEY_EXPORT */ +/* Write a public Curve25519 key to DER format, + * length on success else < 0 */ +int wc_Curve25519PublicKeyToDer(curve25519_key* key, byte* output, word32 inLen, + int withAlg) +{ + int ret; + byte pubKey[CURVE25519_KEYSIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); -#endif /* HAVE_ED25519 */ + if (key == NULL || output == NULL) { + return BAD_FUNC_ARG; + } -#ifdef HAVE_ED448 + ret = wc_curve25519_export_public(key, pubKey, &pubKeyLen); + if (ret == 0) { + ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, inLen, + X25519k, withAlg); + } + return ret; +} +#endif /* HAVE_CURVE25519 && HAVE_CURVE25519_KEY_EXPORT */ -#ifdef HAVE_ED448_KEY_IMPORT +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT) int wc_Ed448PrivateKeyDecode(const byte* input, word32* inOutIdx, - ed448_key* key, word32 inSz) + ed448_key* key, word32 inSz) { -#ifndef WOLFSSL_ASN_TEMPLATE - word32 oid; - int ret, version, length, endKeyIdx, privSz, pubSz; - const byte* priv; - const byte* pub; + int ret; + byte privKey[ED448_KEY_SIZE], pubKey[ED448_PUB_KEY_SIZE]; + word32 privKeyLen = (word32)sizeof(privKey); + word32 pubKeyLen = (word32)sizeof(pubKey); - if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { return BAD_FUNC_ARG; - - if (GetSequence(input, inOutIdx, &length, inSz) >= 0) { - endKeyIdx = *inOutIdx + length; - - if (GetMyVersion(input, inOutIdx, &version, inSz) < 0) - return ASN_PARSE_E; - if (version != 0) { - WOLFSSL_MSG("Unrecognized version of ED448 private key"); - return ASN_PARSE_E; - } - - if (GetAlgoId(input, inOutIdx, &oid, oidKeyType, inSz) < 0) - return ASN_PARSE_E; - if (oid != ED448k) - return ASN_PARSE_E; - - if (GetOctetString(input, inOutIdx, &length, inSz) < 0) - return ASN_PARSE_E; - - if (GetOctetString(input, inOutIdx, &privSz, inSz) < 0) - return ASN_PARSE_E; - - priv = input + *inOutIdx; - *inOutIdx += privSz; - } - else { - if (GetOctetString(input, inOutIdx, &privSz, inSz) < 0) - return ASN_PARSE_E; - - if (privSz != 57) - return ASN_PARSE_E; - - priv = input + *inOutIdx; - *inOutIdx += privSz; - endKeyIdx = *inOutIdx; } - if (endKeyIdx == (int)*inOutIdx) { - ret = wc_ed448_import_private_only(priv, privSz, key); - } - else { - if (GetASNHeader(input, ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1, - inOutIdx, &length, inSz) < 0) { - return ASN_PARSE_E; - } - if (GetOctetString(input, inOutIdx, &pubSz, inSz) < 0) - return ASN_PARSE_E; - pub = input + *inOutIdx; - *inOutIdx += pubSz; - - ret = wc_ed448_import_private_key(priv, privSz, pub, pubSz, key); - } - if (ret == 0 && endKeyIdx != (int)*inOutIdx) - return ASN_PARSE_E; - - return ret; -#else - DECL_ASNGETDATA(dataASN, edKeyASN_Length); - int ret = 0; - - CALLOC_ASNGETDATA(dataASN, edKeyASN_Length, ret, NULL); - + ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen, + pubKey, &pubKeyLen, ED448k); if (ret == 0) { - /* Require Ed448 OID. */ - GetASN_ExpBuffer(&dataASN[3], keyEd448Oid, sizeof(keyEd448Oid)); - /* Parse full private key. */ - ret = GetASN_Items(edKeyASN, dataASN, edKeyASN_Length, 1, input, - inOutIdx, inSz); - if (ret != 0) { - /* Parse just the OCTET_STRING. */ - ret = GetASN_Items(&edKeyASN[5], &dataASN[5], 1, 0, input, inOutIdx, - inSz); - if (ret != 0) { - ret = ASN_PARSE_E; - } - } - } - /* Check the private value length is correct. */ - if ((ret == 0) && dataASN[5].data.ref.length != ED448_KEY_SIZE) { - ret = ASN_PARSE_E; - } - if ((ret == 0) && dataASN[7].tag == 0) { - /* Import just the private value. */ - ret = wc_ed448_import_private_only(dataASN[5].data.ref.data, - ED448_KEY_SIZE, key); - } - else if ((ret == 0) && (dataASN[8].data.ref.length != ED448_PUB_KEY_SIZE)) { - ret = ASN_PARSE_E; - } - else if (ret == 0) { - /* Import private and public value. */ - ret = wc_ed448_import_private_key(dataASN[5].data.ref.data, - ED448_KEY_SIZE, dataASN[8].data.ref.data, ED448_PUB_KEY_SIZE, - key); - } - - FREE_ASNGETDATA(dataASN, NULL); - return ret; -#endif /* WOLFSSL_ASN_TEMPLATE */ -} - - -int wc_Ed448PublicKeyDecode(const byte* input, word32* inOutIdx, - ed448_key* key, word32 inSz) -{ -#ifndef WOLFSSL_ASN_TEMPLATE - int length; - int ret; - - if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) - return BAD_FUNC_ARG; - - if (GetSequence(input, inOutIdx, &length, inSz) < 0) - return ASN_PARSE_E; - - if (GetSequence(input, inOutIdx, &length, inSz) < 0) - return ASN_PARSE_E; - - ret = SkipObjectId(input, inOutIdx, inSz); - if (ret != 0) - return ret; - - /* key header */ - ret = CheckBitString(input, inOutIdx, NULL, inSz, 1, NULL); - if (ret != 0) - return ret; - - /* This is the raw point data compressed or uncompressed. */ - if (wc_ed448_import_public(input + *inOutIdx, inSz - *inOutIdx, key) != 0) - return ASN_ECC_KEY_E; - - return 0; -#else - DECL_ASNGETDATA(dataASN, edPubKeyASN_Length); - int ret = 0; - - CALLOC_ASNGETDATA(dataASN, edPubKeyASN_Length, ret, NULL); - - if (ret == 0) { - /* Require Ed448 OID. */ - GetASN_ExpBuffer(&dataASN[2], keyEd448Oid, sizeof(keyEd448Oid)); - /* Decode Ed448 private key. */ - ret = GetASN_Items(edPubKeyASN, dataASN, edPubKeyASN_Length, 1, input, - inOutIdx, inSz); - if (ret != 0) { - ret = ASN_PARSE_E; - } - } - /* Check the public value length is correct. */ - if ((ret == 0) && (dataASN[3].data.ref.length != ED448_PUB_KEY_SIZE)) { - ret = ASN_PARSE_E; - } - if (ret == 0) { - /* Import the public value. */ - ret = wc_ed448_import_public(dataASN[3].data.ref.data, - ED448_PUB_KEY_SIZE, key); - if (ret != 0) { - ret = ASN_ECC_KEY_E; - } - } - - FREE_ASNGETDATA(dataASN, NULL); - return ret; -#endif /* WOLFSSL_ASN_TEMPLATE */ -} -#endif /* HAVE_ED448_KEY_IMPORT */ - -#if defined(WOLFSSL_KEY_GEN) && defined(HAVE_ED448_KEY_EXPORT) - -/* build DER formatted ED448 key, - * return length on success, negative on error */ -static int wc_BuildEd448KeyDer(ed448_key* key, byte* output, word32 inLen, - int pubOut) -{ -#ifndef WOLFSSL_ASN_TEMPLATE - byte algoArray[MAX_ALGO_SZ]; - byte ver[MAX_VERSION_SZ]; - byte seq[MAX_SEQ_SZ]; - int ret; - word32 idx = 0, seqSz, verSz, algoSz, privSz, pubSz = 0; - - if (key == NULL || output == NULL || inLen == 0) - return BAD_FUNC_ARG; - - if (pubOut) { - pubSz = 2 + 2 + ED448_PUB_KEY_SIZE; - } - privSz = 2 + 2 + ED448_KEY_SIZE; - algoSz = SetAlgoID(ED448k, algoArray, oidKeyType, 0); - verSz = SetMyVersion(0, ver, FALSE); - seqSz = SetSequence(verSz + algoSz + privSz + pubSz, seq); - - if (seqSz + verSz + algoSz + privSz + pubSz > inLen) - return BAD_FUNC_ARG; - - /* write out */ - /* seq */ - XMEMCPY(output + idx, seq, seqSz); - idx = seqSz; - /* ver */ - XMEMCPY(output + idx, ver, verSz); - idx += verSz; - /* algo */ - XMEMCPY(output + idx, algoArray, algoSz); - idx += algoSz; - /* privKey */ - idx += SetOctetString(2 + ED448_KEY_SIZE, output + idx); - idx += SetOctetString(ED448_KEY_SIZE, output + idx); - ret = wc_ed448_export_private_only(key, output + idx, &privSz); - if (ret != 0) - return ret; - idx += privSz; - /* pubKey */ - if (pubOut) { - idx += SetExplicit(1, 2 + ED448_PUB_KEY_SIZE, output + idx); - idx += SetOctetString(ED448_KEY_SIZE, output + idx); - ret = wc_ed448_export_public(key, output + idx, &pubSz); - if (ret != 0) - return ret; - idx += pubSz; - } - - return idx; -#else - DECL_ASNSETDATA(dataASN, edKeyASN_Length); - word32 privSz; - int ret = 0; - int sz; - - /* Validate parameters. */ - if ((key == NULL) || (output == NULL) || (inLen == 0)) { - ret = BAD_FUNC_ARG; - } - - CALLOC_ASNSETDATA(dataASN, edKeyASN_Length, ret, NULL); - - if (ret == 0) { - /* Set Ed448 OID. */ - SetASN_Buffer(&dataASN[1], keyEd448Oid, sizeof(keyEd448Oid)); - /* Leave space for private key. */ - SetASN_Buffer(&dataASN[5], NULL, ED448_KEY_SIZE); - /* Don't write out attributes. */ - dataASN[6].noOut = 1; - if (pubOut) { - /* Leave space for public key. */ - SetASN_Buffer(&dataASN[8], NULL, ED448_PUB_KEY_SIZE); + if (pubKeyLen == 0) { + ret = wc_ed448_import_private_only(privKey, privKeyLen, key); } else { - /* Don't put out public part. */ - dataASN[7].noOut = dataASN[8].noOut = 1; + ret = wc_ed448_import_private_key(privKey, privKeyLen, + pubKey, pubKeyLen, key); } - - /* Calculate the size of encoding. */ - ret = SizeASN_Items(edKeyASN, dataASN, edKeyASN_Length, &sz); } - /* Check buffer is big enough. */ - if ((ret == 0) && (sz > (int)inLen)) { - ret = BAD_FUNC_ARG; - } - if (ret == 0) { - /* Encode Ed448 private key. */ - SetASN_Items(edKeyASN, dataASN, edKeyASN_Length, output); - - /* Put private value into space provided. */ - privSz = ED448_KEY_SIZE; - ret = wc_ed448_export_private_only(key, - (byte*)dataASN[5].data.buffer.data, &privSz); - } - if ((ret == 0) && pubOut) { - /* Put public value into space provided. */ - word32 pubSz = ED448_PUB_KEY_SIZE; - ret = wc_ed448_export_public(key, (byte*)dataASN[8].data.buffer.data, - &pubSz); - } - - if (ret == 0) { - /* Return size of encoding. */ - ret = sz; - } - - FREE_ASNSETDATA(dataASN, NULL); return ret; -#endif } +int wc_Ed448PublicKeyDecode(const byte* input, word32* inOutIdx, + ed448_key* key, word32 inSz) +{ + int ret; + byte pubKey[ED448_PUB_KEY_SIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); + + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { + return BAD_FUNC_ARG; + } + + ret = DecodeAsymKeyPublic(input, inOutIdx, inSz, + pubKey, &pubKeyLen, ED448k); + if (ret == 0) { + ret = wc_ed448_import_public(pubKey, pubKeyLen, key); + } + return ret; +} +#endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT */ + +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT) +int wc_Curve448PrivateKeyDecode(const byte* input, word32* inOutIdx, + curve448_key* key, word32 inSz) +{ + int ret; + byte privKey[CURVE448_KEY_SIZE]; + word32 privKeyLen = CURVE448_KEY_SIZE; + + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { + return BAD_FUNC_ARG; + } + + ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen, + NULL, NULL, X448k); + if (ret == 0) { + ret = wc_curve448_import_private(privKey, privKeyLen, key); + } + return ret; +} + +int wc_Curve448PublicKeyDecode(const byte* input, word32* inOutIdx, + curve448_key* key, word32 inSz) +{ + int ret; + byte pubKey[CURVE448_PUB_KEY_SIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); + + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { + return BAD_FUNC_ARG; + } + + ret = DecodeAsymKeyPublic(input, inOutIdx, inSz, + pubKey, &pubKeyLen, X448k); + if (ret == 0) { + ret = wc_curve448_import_public(pubKey, pubKeyLen, key); + } + return ret; +} +#endif /* HAVE_CURVE448 && HAVE_ED448_KEY_IMPORT */ + +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) /* Write a Private ecc key, including public to DER format, * length on success else < 0 */ int wc_Ed448KeyToDer(ed448_key* key, byte* output, word32 inLen) { - return wc_BuildEd448KeyDer(key, output, inLen, 1); + if (key == NULL) { + return BAD_FUNC_ARG; + } + return SetAsymKeyDer(key->k, ED448_KEY_SIZE, + key->p, ED448_KEY_SIZE, output, inLen, ED448k); } - - /* Write only private ecc key to DER format, * length on success else < 0 */ int wc_Ed448PrivateKeyToDer(ed448_key* key, byte* output, word32 inLen) { - return wc_BuildEd448KeyDer(key, output, inLen, 0); + if (key == NULL) { + return BAD_FUNC_ARG; + } + return SetAsymKeyDer(key->k, ED448_KEY_SIZE, + NULL, 0, output, inLen, ED448k); } -#endif /* WOLFSSL_KEY_GEN && HAVE_ED448_KEY_EXPORT */ +#endif /* HAVE_ED448 && HAVE_ED448_KEY_EXPORT */ -#endif /* HAVE_ED448 */ +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT) +/* Write private Curve448 key to DER format, + * length on success else < 0 */ +int wc_Curve448PrivateKeyToDer(curve448_key* key, byte* output, word32 inLen) +{ + int ret; + byte privKey[CURVE448_KEY_SIZE]; + word32 privKeyLen = CURVE448_KEY_SIZE; -#if (defined(HAVE_OCSP) || defined(HAVE_CRL)) && !defined(WOLFSSL_ASN_TEMPLATE) + if (key == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_curve448_export_private_raw(key, privKey, &privKeyLen); + if (ret == 0) { + ret = SetAsymKeyDer(privKey, privKeyLen, NULL, 0, output, inLen, + X448k); + } + return ret; +} +/* Write a public Curve448 key to DER format, + * length on success else < 0 */ +int wc_Curve448PublicKeyToDer(curve448_key* key, byte* output, word32 inLen, + int withAlg) +{ + int ret; + byte pubKey[CURVE448_PUB_KEY_SIZE]; + word32 pubKeyLen = (word32)sizeof(pubKey); + + if (key == NULL || output == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_curve448_export_public(key, pubKey, &pubKeyLen); + if (ret == 0) { + ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, inLen, + X448k, withAlg); + } + return ret; +} +#endif /* HAVE_CURVE448 && HAVE_CURVE448_KEY_EXPORT */ + + +#if defined(HAVE_OCSP) || defined(HAVE_CRL) /* Get raw Date only, no processing, 0 on success */ static int GetBasicDate(const byte* source, word32* idx, byte* date, diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 07f1696b2d..fe8afd3176 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -57,6 +57,41 @@ const curve25519_set_type curve25519_sets[] = { static const unsigned char kCurve25519BasePoint[CURVE25519_KEYSIZE] = {9}; +/* Curve25519 private key must be less than order */ +/* These functions clamp private k and check it */ +static WC_INLINE int curve25519_priv_clamp(byte* priv) +{ + priv[0] &= 248; + priv[CURVE25519_KEYSIZE-1] &= 127; + priv[CURVE25519_KEYSIZE-1] |= 64; + return 0; +} +static WC_INLINE int curve25519_priv_clamp_check(const byte* priv) +{ + /* check that private part of key has been clamped */ + int ret = 0; + if ((priv[0] & ~248) || + (priv[CURVE25519_KEYSIZE-1] & 128)) { + ret = ECC_BAD_ARG_E; + } + return ret; +} + +static WC_INLINE void curve25519_copy_point(byte* out, const byte* point, + int endian) +{ + int i; + if (endian == EC25519_BIG_ENDIAN) { + /* put shared secret key in Big Endian format */ + for (i = 0; i < CURVE25519_KEYSIZE; i++) { + out[i] = point[CURVE25519_KEYSIZE - i -1]; + } + } + else { /* put shared secret key in Little Endian format */ + XMEMCPY(out, point, CURVE25519_KEYSIZE); + } +} + /* compute the public key from an existing private key, using bare vectors. * * return value is propagated from curve25519() (0 on success), or @@ -66,28 +101,29 @@ int wc_curve25519_make_pub(int public_size, byte* pub, int private_size, const byte* priv) { int ret; +#ifdef FREESCALE_LTC_ECC + const ECPoint* basepoint = nxp_ltc_curve25519_GetBasePoint(); + ECPoint wc_pub; +#endif - if ((public_size != CURVE25519_KEYSIZE) || + if ( (public_size != CURVE25519_KEYSIZE) || (private_size != CURVE25519_KEYSIZE)) { return ECC_BAD_ARG_E; } - if ((pub == NULL) || (priv == NULL)) - return ECC_BAD_ARG_E; - - /* check clamping */ - if ((priv[0] & ~248) || - (priv[CURVE25519_KEYSIZE-1] & 128)) { + if ((pub == NULL) || (priv == NULL)) { return ECC_BAD_ARG_E; } + /* check clamping */ + ret = curve25519_priv_clamp_check(priv); + if (ret != 0) + return ret; + #ifdef FREESCALE_LTC_ECC - { - const ECPoint* basepoint = nxp_ltc_curve25519_GetBasePoint(); - ECPoint wc_pub; - /* input basepoint on Weierstrass curve */ - ret = nxp_ltc_curve25519(&wc_pub, priv, basepoint, kLTC_Weierstrass); - if (ret == 0) - XMEMCPY(pub, wc_pub.point, CURVE25519_KEYSIZE); + /* input basepoint on Weierstrass curve */ + ret = nxp_ltc_curve25519(&wc_pub, priv, basepoint, kLTC_Weierstrass); + if (ret == 0) { + XMEMCPY(pub, wc_pub.point, CURVE25519_KEYSIZE); } #else fe_init(); @@ -117,7 +153,7 @@ int wc_curve25519_generic(int public_size, byte* pub, int basepoint_size, const byte* basepoint) { #ifdef FREESCALE_LTC_ECC - /* unsupported with NXP LTC, onlly supports single basepoint with + /* unsupported with NXP LTC, only supports single basepoint with * nxp_ltc_curve25519_GetBasePoint() */ return WC_HW_E; #else @@ -132,10 +168,9 @@ int wc_curve25519_generic(int public_size, byte* pub, return ECC_BAD_ARG_E; /* check clamping */ - if ((priv[0] & ~248) || - (priv[CURVE25519_KEYSIZE-1] & 128)) { - return ECC_BAD_ARG_E; - } + ret = curve25519_priv_clamp_check(priv); + if (ret != 0) + return ret; fe_init(); @@ -171,15 +206,12 @@ int wc_curve25519_make_priv(WC_RNG* rng, int keysize, byte* key) /* random number for private key */ ret = wc_RNG_GenerateBlock(rng, key, keysize); - if (ret != 0) - return ret; + if (ret == 0) { + /* Clamp the private key */ + ret = curve25519_priv_clamp(key); + } - /* Clamp the private key */ - key[0] &= 248; - key[CURVE25519_KEYSIZE-1] &= 63; /* same &=127 because |=64 after */ - key[CURVE25519_KEYSIZE-1] |= 64; - - return 0; + return ret; } /* generate a new keypair. @@ -203,11 +235,14 @@ int wc_curve25519_make_key(WC_RNG* rng, int keysize, curve25519_key* key) } #endif - ret = wc_curve25519_make_priv(rng, keysize, key->k.point); - if (ret < 0) - return ret; - return wc_curve25519_make_pub((int)sizeof(key->p.point), key->p.point, - (int)sizeof(key->k.point), key->k.point); + ret = wc_curve25519_make_priv(rng, keysize, key->k); + if (ret == 0) { + key->privSet = 1; + ret = wc_curve25519_make_pub((int)sizeof(key->p.point), key->p.point, + (int)sizeof(key->k), key->k); + key->pubSet = (ret == 0); + } + return ret; } #ifdef HAVE_CURVE25519_SHARED_SECRET @@ -224,11 +259,7 @@ int wc_curve25519_shared_secret_ex(curve25519_key* private_key, curve25519_key* public_key, byte* out, word32* outlen, int endian) { -#ifdef FREESCALE_LTC_ECC - ECPoint o = {{0}}; -#else - unsigned char o[CURVE25519_KEYSIZE]; -#endif + ECPoint o; int ret = 0; /* sanity check */ @@ -237,10 +268,17 @@ int wc_curve25519_shared_secret_ex(curve25519_key* private_key, return BAD_FUNC_ARG; } - /* avoid implementation fingerprinting */ - if (public_key->p.point[CURVE25519_KEYSIZE-1] > 0x7F) + /* make sure we have a populated private and public key */ + if (!private_key->privSet || !public_key->pubSet) { return ECC_BAD_ARG_E; + } + /* avoid implementation fingerprinting - make sure signed bit is not set */ + if (public_key->p.point[CURVE25519_KEYSIZE-1] & 0x80) { + return ECC_BAD_ARG_E; + } + + XMEMSET(&o, 0, sizeof(o)); #ifdef WOLF_CRYPTO_CB if (private_key->devId != INVALID_DEVID) { @@ -254,54 +292,28 @@ int wc_curve25519_shared_secret_ex(curve25519_key* private_key, #ifdef FREESCALE_LTC_ECC /* input point P on Curve25519 */ - ret = nxp_ltc_curve25519(&o, private_key->k.point, &public_key->p, + ret = nxp_ltc_curve25519(&o, private_key->k, &public_key->p, kLTC_Curve25519); #else #if defined(USE_INTEL_SPEEDUP) || defined(WOLFSSL_ARMASM) SAVE_VECTOR_REGISTERS(); #endif - ret = curve25519(o, private_key->k.point, public_key->p.point); + ret = curve25519(o.point, private_key->k, public_key->p.point); #if defined(USE_INTEL_SPEEDUP) || defined(WOLFSSL_ARMASM) RESTORE_VECTOR_REGISTERS(); #endif #endif if (ret != 0) { - #ifdef FREESCALE_LTC_ECC - ForceZero(o.point, CURVE25519_KEYSIZE); - ForceZero(o.pointY, CURVE25519_KEYSIZE); - #else - ForceZero(o, CURVE25519_KEYSIZE); - #endif + ForceZero(&o, sizeof(o)); return ret; } - if (endian == EC25519_BIG_ENDIAN) { - int i; - /* put shared secret key in Big Endian format */ - for (i = 0; i < CURVE25519_KEYSIZE; i++) - #ifdef FREESCALE_LTC_ECC - out[i] = o.point[CURVE25519_KEYSIZE - i -1]; - #else - out[i] = o[CURVE25519_KEYSIZE - i -1]; - #endif - } - else /* put shared secret key in Little Endian format */ - #ifdef FREESCALE_LTC_ECC - XMEMCPY(out, o.point, CURVE25519_KEYSIZE); - #else - XMEMCPY(out, o, CURVE25519_KEYSIZE); - #endif - + curve25519_copy_point(out, o.point, endian); *outlen = CURVE25519_KEYSIZE; - #ifdef FREESCALE_LTC_ECC - ForceZero(o.point, CURVE25519_KEYSIZE); - ForceZero(o.pointY, CURVE25519_KEYSIZE); - #else - ForceZero(o, CURVE25519_KEYSIZE); - #endif + ForceZero(&o, sizeof(o)); return ret; } @@ -322,27 +334,29 @@ int wc_curve25519_export_public(curve25519_key* key, byte* out, word32* outLen) int wc_curve25519_export_public_ex(curve25519_key* key, byte* out, word32* outLen, int endian) { - if (key == NULL || out == NULL || outLen == NULL) + int ret = 0; + + if (key == NULL || out == NULL || outLen == NULL) { return BAD_FUNC_ARG; + } /* check and set outgoing key size */ if (*outLen < CURVE25519_KEYSIZE) { *outLen = CURVE25519_KEYSIZE; return ECC_BAD_ARG_E; } + + /* calculate public if missing */ + if (!key->pubSet) { + ret = wc_curve25519_make_pub((int)sizeof(key->p.point), key->p.point, + (int)sizeof(key->k), key->k); + key->pubSet = (ret == 0); + } + /* export public point with endianess */ + curve25519_copy_point(out, key->p.point, endian); *outLen = CURVE25519_KEYSIZE; - if (endian == EC25519_BIG_ENDIAN) { - int i; - - /* read keys in Big Endian format */ - for (i = 0; i < CURVE25519_KEYSIZE; i++) - out[i] = key->p.point[CURVE25519_KEYSIZE - i - 1]; - } - else - XMEMCPY(out, key->p.point, CURVE25519_KEYSIZE); - - return 0; + return ret; } #endif /* HAVE_CURVE25519_KEY_EXPORT */ @@ -362,33 +376,32 @@ int wc_curve25519_import_public(const byte* in, word32 inLen, int wc_curve25519_import_public_ex(const byte* in, word32 inLen, curve25519_key* key, int endian) { +#ifdef FREESCALE_LTC_ECC + ltc_pkha_ecc_point_t ltcPoint; +#endif + /* sanity check */ - if (key == NULL || in == NULL) + if (key == NULL || in == NULL) { return BAD_FUNC_ARG; + } /* check size of incoming keys */ - if (inLen != CURVE25519_KEYSIZE) + if (inLen != CURVE25519_KEYSIZE) { return ECC_BAD_ARG_E; - - if (endian == EC25519_BIG_ENDIAN) { - int i; - - /* read keys in Big Endian format */ - for (i = 0; i < CURVE25519_KEYSIZE; i++) - key->p.point[i] = in[CURVE25519_KEYSIZE - i - 1]; } - else - XMEMCPY(key->p.point, in, inLen); + + /* import public point with endianess */ + curve25519_copy_point(key->p.point, in, endian); + key->pubSet = 1; key->dp = &curve25519_sets[0]; /* LTC needs also Y coordinate - let's compute it */ - #ifdef FREESCALE_LTC_ECC - ltc_pkha_ecc_point_t ltcPoint; - ltcPoint.X = &key->p.point[0]; - ltcPoint.Y = &key->p.pointY[0]; - LTC_PKHA_Curve25519ComputeY(<cPoint); - #endif +#ifdef FREESCALE_LTC_ECC + ltcPoint.X = &key->p.point[0]; + ltcPoint.Y = &key->p.pointY[0]; + LTC_PKHA_Curve25519ComputeY(<cPoint); +#endif return 0; } @@ -481,18 +494,11 @@ int wc_curve25519_export_private_raw_ex(curve25519_key* key, byte* out, *outLen = CURVE25519_KEYSIZE; return ECC_BAD_ARG_E; } + + /* export private scalar with endianess */ + curve25519_copy_point(out, key->k, endian); *outLen = CURVE25519_KEYSIZE; - if (endian == EC25519_BIG_ENDIAN) { - int i; - - /* put the key in Big Endian format */ - for (i = 0; i < CURVE25519_KEYSIZE; i++) - out[i] = key->k.point[CURVE25519_KEYSIZE - i - 1]; - } - else - XMEMCPY(out, key->k.point, CURVE25519_KEYSIZE); - return 0; } @@ -572,31 +578,23 @@ int wc_curve25519_import_private_ex(const byte* priv, word32 privSz, curve25519_key* key, int endian) { /* sanity check */ - if (key == NULL || priv == NULL) + if (key == NULL || priv == NULL) { return BAD_FUNC_ARG; + } /* check size of incoming keys */ - if ((int)privSz != CURVE25519_KEYSIZE) + if ((int)privSz != CURVE25519_KEYSIZE) { return ECC_BAD_ARG_E; - - if (endian == EC25519_BIG_ENDIAN) { - int i; - - /* read the key in Big Endian format */ - for (i = 0; i < CURVE25519_KEYSIZE; i++) - key->k.point[i] = priv[CURVE25519_KEYSIZE - i - 1]; } - else - XMEMCPY(key->k.point, priv, CURVE25519_KEYSIZE); + + /* import private scalar with endianess */ + curve25519_copy_point(key->k, priv, endian); + key->privSet = 1; key->dp = &curve25519_sets[0]; /* Clamp the key */ - key->k.point[0] &= 248; - key->k.point[privSz-1] &= 63; /* same &=127 because |=64 after */ - key->k.point[privSz-1] |= 64; - - return 0; + return curve25519_priv_clamp(key->k); } #endif /* HAVE_CURVE25519_KEY_IMPORT */ @@ -633,19 +631,16 @@ int wc_curve25519_init(curve25519_key* key) /* Clean the memory of a key */ void wc_curve25519_free(curve25519_key* key) { - if (key == NULL) + if (key == NULL) return; - key->dp = NULL; - ForceZero(key->p.point, sizeof(key->p.point)); - ForceZero(key->k.point, sizeof(key->k.point)); - #ifdef FREESCALE_LTC_ECC - ForceZero(key->p.point, sizeof(key->p.pointY)); - ForceZero(key->k.point, sizeof(key->k.pointY)); - #endif + key->dp = NULL; + ForceZero(key->k, sizeof(key->k)); + XMEMSET(&key->p, 0, sizeof(key->p)); + key->pubSet = 0; + key->privSet = 0; } - /* get key size */ int wc_curve25519_size(curve25519_key* key) { @@ -656,4 +651,3 @@ int wc_curve25519_size(curve25519_key* key) } #endif /*HAVE_CURVE25519*/ - diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index e926351fd2..3a6389cff3 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -42,6 +42,28 @@ #include #endif +int wc_curve448_make_pub(int public_size, byte* pub, int private_size, + const byte* priv) +{ + int ret; + unsigned char basepoint[CURVE448_KEY_SIZE] = {5}; + + if ((pub == NULL) || (priv == NULL)) { + return ECC_BAD_ARG_E; + } + if ((public_size != CURVE448_PUB_KEY_SIZE) || + (private_size != CURVE448_KEY_SIZE)) { + return ECC_BAD_ARG_E; + } + + fe448_init(); + + /* compute public key */ + ret = curve448(pub, priv, basepoint); + + return ret; +} + /* Make a new curve448 private/public key. * @@ -54,7 +76,6 @@ */ int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key) { - unsigned char basepoint[CURVE448_KEY_SIZE] = {5}; int ret = 0; if ((key == NULL) || (rng == NULL)) { @@ -67,21 +88,25 @@ int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key) } if (ret == 0) { - fe448_init(); - /* random number for private key */ ret = wc_RNG_GenerateBlock(rng, key->k, keysize); } if (ret == 0) { - /* Clamp the private key */ + key->privSet = 1; + + /* clamp private */ key->k[0] &= 0xfc; key->k[CURVE448_KEY_SIZE-1] |= 0x80; - - /* compute public key */ - ret = curve448(key->p, key->k, basepoint); - if (ret != 0) { - ForceZero(key->k, keysize); - ForceZero(key->p, keysize); + + /* compute public */ + ret = wc_curve448_make_pub((int)sizeof(key->p), key->p, + (int)sizeof(key->k), key->k); + if (ret == 0) { + key->pubSet = 1; + } + else { + ForceZero(key->k, sizeof(key->k)); + XMEMSET(key->p, 0, sizeof(key->p)); } } @@ -137,6 +162,10 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, (outLen == NULL) || (*outLen < CURVE448_PUB_KEY_SIZE)) { ret = BAD_FUNC_ARG; } + /* make sure we have a populated private and public key */ + if (ret == 0 && (!private_key->privSet || !public_key->pubSet)) { + ret = ECC_BAD_ARG_E; + } if (ret == 0) { ret = curve448(o, private_key->k, public_key->p); @@ -207,9 +236,16 @@ int wc_curve448_export_public_ex(curve448_key* key, byte* out, word32* outLen, *outLen = CURVE448_PUB_KEY_SIZE; ret = ECC_BAD_ARG_E; } + if (ret == 0) { + /* calculate public if missing */ + if (!key->pubSet) { + ret = wc_curve448_make_pub((int)sizeof(key->p), key->p, + (int)sizeof(key->k), key->k); + key->pubSet = (ret == 0); + } + } if (ret == 0) { *outLen = CURVE448_PUB_KEY_SIZE; - if (endian == EC448_BIG_ENDIAN) { /* read keys in Big Endian format */ for (i = 0; i < CURVE448_PUB_KEY_SIZE; i++) { @@ -278,6 +314,7 @@ int wc_curve448_import_public_ex(const byte* in, word32 inLen, } else XMEMCPY(key->p, in, inLen); + key->pubSet = 1; } return ret; @@ -569,6 +606,8 @@ int wc_curve448_import_private_ex(const byte* priv, word32 privSz, /* Clamp the key */ key->k[0] &= 0xfc; key->k[CURVE448_KEY_SIZE-1] |= 0x80; + + key->privSet = 1; } return ret; @@ -607,10 +646,12 @@ int wc_curve448_init(curve448_key* key) */ void wc_curve448_free(curve448_key* key) { - if (key != NULL) { - ForceZero(key->p, sizeof(key->p)); - ForceZero(key->k, sizeof(key->k)); - } + if (key != NULL) { + ForceZero(key->k, sizeof(key->k)); + XMEMSET(key->p, 0, sizeof(key->p)); + key->pubSet = 0; + key->privSet = 0; + } } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a323d2909f..fa26ff740d 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -16278,7 +16278,11 @@ exit_gen_test: static int dh_generate_test(WC_RNG *rng) { int ret = 0; - DhKey smallKey; +#ifdef WOLFSSL_SMALL_STACK + DhKey *smallKey = (DhKey*)XMALLOC(sizeof(DhKey), HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); +#else + DhKey smallKey[1]; +#endif byte p[2] = { 0, 5 }; byte g[2] = { 0, 2 }; #if !defined(WOLFSSL_SP_MATH) @@ -16294,7 +16298,13 @@ static int dh_generate_test(WC_RNG *rng) word32 pubSz = sizeof(pub); #endif - ret = wc_InitDhKey_ex(&smallKey, HEAP_HINT, devId); +#ifdef WOLFSSL_SMALL_STACK + if (smallKey == NULL) { + ERROR_OUT(-8010, exit_gen_test); + } +#endif + + ret = wc_InitDhKey_ex(smallKey, HEAP_HINT, devId); if (ret != 0) return -8010; @@ -16308,32 +16318,32 @@ static int dh_generate_test(WC_RNG *rng) if (ret != BAD_FUNC_ARG) { ERROR_OUT(-8012, exit_gen_test); } - ret = wc_DhSetKey(&smallKey, NULL, sizeof(p), g, sizeof(g)); + ret = wc_DhSetKey(smallKey, NULL, sizeof(p), g, sizeof(g)); if (ret != BAD_FUNC_ARG) { ERROR_OUT(-8013, exit_gen_test); } - ret = wc_DhSetKey(&smallKey, p, 0, g, sizeof(g)); + ret = wc_DhSetKey(smallKey, p, 0, g, sizeof(g)); if (ret != BAD_FUNC_ARG) { ERROR_OUT(-8014, exit_gen_test); } - ret = wc_DhSetKey(&smallKey, p, sizeof(p), NULL, sizeof(g)); + ret = wc_DhSetKey(smallKey, p, sizeof(p), NULL, sizeof(g)); if (ret != BAD_FUNC_ARG) { ERROR_OUT(-8015, exit_gen_test); } - ret = wc_DhSetKey(&smallKey, p, sizeof(p), g, 0); + ret = wc_DhSetKey(smallKey, p, sizeof(p), g, 0); if (ret != BAD_FUNC_ARG) { ERROR_OUT(-8016, exit_gen_test); } - ret = wc_DhSetKey(&smallKey, p, sizeof(p), g, sizeof(g)); + ret = wc_DhSetKey(smallKey, p, sizeof(p), g, sizeof(g)); if (ret != 0) { ERROR_OUT(-8017, exit_gen_test); } #if !defined(WOLFSSL_SP_MATH) /* Use API. */ - ret = wc_DhGenerateKeyPair(&smallKey, rng, priv, &privSz, pub, &pubSz); + ret = wc_DhGenerateKeyPair(smallKey, rng, priv, &privSz, pub, &pubSz); #if defined(WOLFSSL_ASYNC_CRYPT) - ret = wc_AsyncWait(ret, &smallKey.asyncDev, WC_ASYNC_FLAG_NONE); + ret = wc_AsyncWait(ret, &smallKey->asyncDev, WC_ASYNC_FLAG_NONE); #endif if (ret != 0) { ret = -8018; @@ -16344,7 +16354,12 @@ static int dh_generate_test(WC_RNG *rng) #endif exit_gen_test: - wc_FreeDhKey(&smallKey); + wc_FreeDhKey(smallKey); +#ifdef WOLFSSL_SMALL_STACK + if (smallKey != NULL) { + XFREE(smallKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif return ret; } @@ -24900,6 +24915,83 @@ static int curve25519_check_public_test(void) #endif /* HAVE_CURVE25519_SHARED_SECRET && HAVE_CURVE25519_KEY_IMPORT */ +#if defined(HAVE_CURVE25519_KEY_EXPORT) && defined(HAVE_CURVE25519_KEY_IMPORT) +static int curve255519_der_test(void) +{ + int ret = 0; + /* certs/statickeys/x25519.der */ + const byte kCurve25519PrivDer[] = { + 0x30, 0x2E, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, + 0x04, 0x22, 0x04, 0x20, 0x78, 0x8E, 0x31, 0x5C, 0x33, 0xA9, 0x19, 0xC0, + 0x5E, 0x36, 0x70, 0x1B, 0xA4, 0xE8, 0xEF, 0xC1, 0x89, 0x8C, 0xB3, 0x15, + 0xC6, 0x79, 0xD3, 0xAC, 0x22, 0x00, 0xAE, 0xFA, 0xB3, 0xB7, 0x0F, 0x78 + }; + /* certs/statickeys/x25519-pub.der */ + const byte kCurve25519PubDer[] = { + 0x30, 0x2A, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x03, 0x21, 0x00, + 0x09, 0xBC, 0x8C, 0xC7, 0x45, 0x0D, 0xC1, 0xC2, 0x02, 0x57, 0x9A, 0x68, + 0x3A, 0xFD, 0x7A, 0xA8, 0xA5, 0x2F, 0xF0, 0x99, 0x39, 0x98, 0xEA, 0x26, + 0xA2, 0x5B, 0x38, 0xFD, 0x96, 0xDB, 0x2A, 0x26 + }; + curve25519_key key; + byte output[128]; + word32 outputSz = 128; + word32 idx; + + if (wc_curve25519_init_ex(&key, HEAP_HINT, devId) != 0) { + return -10723; + } + + /* Test decode / encode of Curve25519 private key only */ + if (ret == 0) { + idx = 0; + ret = wc_Curve25519PrivateKeyDecode(kCurve25519PrivDer, &idx, &key, + (word32)sizeof(kCurve25519PrivDer)); + } + if (ret == 0) { + outputSz = (word32)sizeof(output); + ret = wc_Curve25519PrivateKeyToDer(&key, output, outputSz); + if (ret >= 0) { + outputSz = ret; + ret = 0; + } + else { + ret = -10724; + } + } + if (ret == 0 && (outputSz != (word32)sizeof(kCurve25519PrivDer) || + XMEMCMP(output, kCurve25519PrivDer, outputSz) != 0)) { + ret = -10725; + } + + /* Test decode / encode of Curve25519 public key only */ + if (ret == 0) { + idx = 0; + ret = wc_Curve25519PublicKeyDecode(kCurve25519PubDer, &idx, &key, + (word32)sizeof(kCurve25519PubDer)); + } + if (ret == 0) { + outputSz = (word32)sizeof(output); + ret = wc_Curve25519PublicKeyToDer(&key, output, outputSz, 1); + if (ret >= 0) { + outputSz = ret; + ret = 0; + } + else { + ret = -10726; + } + } + if (ret == 0 && (outputSz != (word32)sizeof(kCurve25519PubDer) || + XMEMCMP(output, kCurve25519PubDer, outputSz) != 0)) { + ret = -10727; + } + + wc_curve25519_free(&key); + + return ret; +} +#endif /* HAVE_CURVE25519_KEY_EXPORT && HAVE_CURVE25519_KEY_IMPORT */ + WOLFSSL_TEST_SUBROUTINE int curve25519_test(void) { WC_RNG rng; @@ -25082,6 +25174,12 @@ WOLFSSL_TEST_SUBROUTINE int curve25519_test(void) return ret; #endif /* HAVE_CURVE25519_SHARED_SECRET && HAVE_CURVE25519_KEY_IMPORT */ +#if defined(HAVE_CURVE25519_KEY_IMPORT) && defined(HAVE_CURVE25519_KEY_IMPORT) + ret = curve255519_der_test(); + if (ret != 0) + return ret; +#endif + /* clean up keys when done */ wc_curve25519_free(&pubKey); wc_curve25519_free(&userB); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 4ad2e6d6dc..af99d28a40 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2763,6 +2763,10 @@ typedef struct { #ifdef HAVE_ECC DerBuffer* ecKey; #endif +#ifdef HAVE_CURVE25519 + DerBuffer* x25519Key; +#endif + /* bits */ #ifndef NO_DH byte weOwnDH:1; @@ -2770,6 +2774,9 @@ typedef struct { #ifdef HAVE_ECC byte weOwnEC:1; #endif +#ifdef HAVE_CURVE25519 + byte weOwnX25519:1; +#endif } StaticKeyExchangeInfo_t; #endif diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 1a9829aa31..333e8f66cc 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -47,7 +47,7 @@ that can be serialized and deserialized in a cross-platform way. /* fips declare of RsaPrivateKeyDecode @wc_fips */ #if defined(HAVE_FIPS) && !defined(NO_RSA) && \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) #include #endif @@ -160,7 +160,7 @@ enum ASNItem_DataType { /* A template entry describing an ASN.1 item. */ typedef struct ASNItem { - /* Depth of ASN.1 item - how many consturcted ASN.1 items above. */ + /* Depth of ASN.1 item - how many constructed ASN.1 items above. */ byte depth; /* BER/DER tag to expect. */ byte tag; @@ -232,7 +232,7 @@ typedef struct ASNGetData { struct { /* Buffer to hold ASN.1 data. */ byte* data; - /* Maxumum length of buffer. */ + /* Maximum length of buffer. */ word32* length; } buffer; /* Refernce to ASN.1 item's data. */ @@ -366,7 +366,7 @@ WOLFSSL_LOCAL void SetASN_OID(ASNSetData *dataASN, int oid, int oidType); (dataASN)->data.mp = num; \ } while (0) -/* Setup ASN data item to get a positve or negative number into an mp_int. +/* Setup ASN data item to get a positive or negative number into an mp_int. * * @param [in] dataASN Dynamic ASN data item. * @param [in] num Multi-precision number object. @@ -572,7 +572,7 @@ WOLFSSL_LOCAL void SetASN_OID(ASNSetData *dataASN, int oid, int oidType); #define GetASNItem_UnusedBits(dataASN) \ (*(dataASN.data.ref.data - 1)) -/* Set the data items at indeces start to end inclusive to not be encoded. +/* Set the data items at indices start to end inclusive to not be encoded. * * @param [in] dataASN Dynamic ASN data item. * @param [in] start First item not to be encoded. @@ -962,8 +962,10 @@ enum Key_Sum { RSAk = 645, NTRUk = 274, ECDSAk = 518, - ED25519k = 256, - ED448k = 257, + ED25519k = 256, /* 1.3.101.112 */ + X25519k = 254, /* 1.3.101.110 */ + ED448k = 257, /* 1.3.101.113 */ + X448k = 255, /* 1.3.101.111 */ DHk = 647, /* dhKeyAgreement OID: 1.2.840.113549.1.3.1 */ }; @@ -1031,8 +1033,11 @@ enum Extensions_Sum { ISSUE_ALT_NAMES_OID = 132, /* 2.5.29.18 */ TLS_FEATURE_OID = 92, /* 1.3.6.1.5.5.7.1.24 */ NETSCAPE_CT_OID = 753, /* 2.16.840.1.113730.1.1 */ - OCSP_NOCHECK_OID = 121 /* 1.3.6.1.5.5.7.48.1.5 + OCSP_NOCHECK_OID = 121, /* 1.3.6.1.5.5.7.48.1.5 id-pkix-ocsp-nocheck */ + + AKEY_PACKAGE_OID = 1048 /* 2.16.840.1.101.2.1.2.78.5 + RFC 5958 - Asymmetric Key Packages */ }; enum CertificatePolicy_Sum { @@ -1886,7 +1891,7 @@ enum Ocsp_Response_Status { OCSP_INTERNAL_ERROR = 2, /* Internal error in issuer */ OCSP_TRY_LATER = 3, /* Try again later */ OCSP_SIG_REQUIRED = 5, /* Must sign the request (4 is skipped) */ - OCSP_UNAUTHROIZED = 6 /* Request unauthorized */ + OCSP_UNAUTHORIZED = 6 /* Request unauthorized */ }; @@ -1929,7 +1934,8 @@ struct CertStatus { byte nextDate[MAX_DATE_SIZE]; byte thisDateFormat; byte nextDateFormat; -#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) +#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \ + defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) WOLFSSL_ASN1_TIME thisDateParsed; WOLFSSL_ASN1_TIME nextDateParsed; byte* thisDateAsn; diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 9acbd60f79..e8f0be466f 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -47,10 +47,18 @@ This library defines the interface APIs for X509 certificates. typedef struct ed25519_key ed25519_key; #define WC_ED25519KEY_TYPE_DEFINED #endif +#ifndef WC_CURVE25519KEY_TYPE_DEFINED + typedef struct curve25519_key curve25519_key; + #define WC_CURVE25519KEY_TYPE_DEFINED +#endif #ifndef WC_ED448KEY_TYPE_DEFINED typedef struct ed448_key ed448_key; #define WC_ED448KEY_TYPE_DEFINED #endif +#ifndef WC_CURVE448KEY_TYPE_DEFINED + typedef struct curve448_key curve448_key; + #define WC_CURVE448KEY_TYPE_DEFINED +#endif #ifndef WC_RSAKEY_TYPE_DEFINED typedef struct RsaKey RsaKey; #define WC_RSAKEY_TYPE_DEFINED @@ -571,54 +579,69 @@ WOLFSSL_API int wc_DhPrivKeyToDer(DhKey* key, byte* out, word32* outSz); WOLFSSL_API int wc_EccPublicKeyDerSize(ecc_key*, int with_AlgCurve); #endif +/* RFC 5958 (Asymmetric Key Packages) */ +#if !defined(WC_ENABLE_ASYM_KEY_EXPORT) && \ + ((defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)) || \ + (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT)) || \ + (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)) || \ + (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT))) + #define WC_ENABLE_ASYM_KEY_EXPORT +#endif + +#if !defined(WC_ENABLE_ASYM_KEY_IMPORT) && \ + ((defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)) || \ + (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT)) || \ + (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)) || \ + (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT))) + #define WC_ENABLE_ASYM_KEY_IMPORT +#endif + #ifdef HAVE_ED25519 - /* private key helpers */ #ifdef HAVE_ED25519_KEY_IMPORT - WOLFSSL_API int wc_Ed25519PrivateKeyDecode(const byte*, word32*, - ed25519_key*, word32); +WOLFSSL_API int wc_Ed25519PrivateKeyDecode(const byte*, word32*, ed25519_key*, word32); +WOLFSSL_API int wc_Ed25519PublicKeyDecode(const byte*, word32*, ed25519_key*, word32); #endif - #ifdef HAVE_ED25519_KEY_EXPORT - WOLFSSL_API int wc_Ed25519KeyToDer(ed25519_key* key, byte* output, - word32 inLen); - WOLFSSL_API int wc_Ed25519PrivateKeyToDer(ed25519_key* key, byte* output, - word32 inLen); +WOLFSSL_API int wc_Ed25519KeyToDer(ed25519_key*, byte*, word32); +WOLFSSL_API int wc_Ed25519PrivateKeyToDer(ed25519_key*, byte*, word32); +WOLFSSL_API int wc_Ed25519PublicKeyToDer(ed25519_key*, byte*, word32, int); #endif +#endif /* HAVE_ED25519 */ - /* public key helper */ - WOLFSSL_API int wc_Ed25519PublicKeyDecode(const byte*, word32*, - ed25519_key*, word32); - #if (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_KEY_GEN)) && \ - defined(HAVE_ED25519_KEY_EXPORT) - - WOLFSSL_API int wc_Ed25519PublicKeyToDer(ed25519_key*, byte* output, - word32 inLen, int with_AlgCurve); - #endif +#ifdef HAVE_CURVE25519 +#ifdef HAVE_CURVE25519_KEY_IMPORT +WOLFSSL_API int wc_Curve25519PrivateKeyDecode(const byte*, word32*, curve25519_key*, word32); +WOLFSSL_API int wc_Curve25519PublicKeyDecode(const byte*, word32*, curve25519_key*, word32); #endif +#ifdef HAVE_CURVE25519_KEY_EXPORT +WOLFSSL_API int wc_Curve25519PrivateKeyToDer(curve25519_key*, byte*, word32); +WOLFSSL_API int wc_Curve25519PublicKeyToDer(curve25519_key*, byte*, word32, int); +#endif +#endif /* HAVE_CURVE25519 */ #ifdef HAVE_ED448 - /* private key helpers */ #ifdef HAVE_ED448_KEY_IMPORT - WOLFSSL_API int wc_Ed448PrivateKeyDecode(const byte*, word32*, - ed448_key*, word32); +WOLFSSL_API int wc_Ed448PrivateKeyDecode(const byte*, word32*, ed448_key*, word32); +WOLFSSL_API int wc_Ed448PublicKeyDecode(const byte*, word32*, ed448_key*, word32); #endif - #ifdef HAVE_ED448_KEY_EXPORT - WOLFSSL_API int wc_Ed448KeyToDer(ed448_key* key, byte* output, - word32 inLen); - WOLFSSL_API int wc_Ed448PrivateKeyToDer(ed448_key* key, byte* output, - word32 inLen); +WOLFSSL_API int wc_Ed448KeyToDer(ed448_key*, byte*, word32); +WOLFSSL_API int wc_Ed448PrivateKeyToDer(ed448_key*, byte*, word32); +WOLFSSL_API int wc_Ed448PublicKeyToDer(ed448_key*, byte*, word32, int); #endif +#endif /* HAVE_ED448 */ - /* public key helper */ - WOLFSSL_API int wc_Ed448PublicKeyDecode(const byte*, word32*, - ed448_key*, word32); - #if (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_KEY_GEN)) && \ - defined(HAVE_ED448_KEY_EXPORT) - WOLFSSL_API int wc_Ed448PublicKeyToDer(ed448_key*, byte* output, - word32 inLen, int with_AlgCurve); - #endif +#ifdef HAVE_CURVE448 +#ifdef HAVE_CURVE448_KEY_IMPORT +WOLFSSL_API int wc_Curve448PrivateKeyDecode(const byte*, word32*, curve448_key*, word32); +WOLFSSL_API int wc_Curve448PublicKeyDecode(const byte*, word32*, curve448_key*, word32); #endif +#ifdef HAVE_CURVE448_KEY_EXPORT +WOLFSSL_API int wc_Curve448PrivateKeyToDer(curve448_key*, byte*, word32); +WOLFSSL_API int wc_Curve448PublicKeyToDer(curve448_key*, byte*, word32, int); +#endif +#endif /* HAVE_CURVE448 */ + /* DER encode signature */ WOLFSSL_API word32 wc_EncodeSignature(byte* out, const byte* digest, diff --git a/wolfssl/wolfcrypt/curve25519.h b/wolfssl/wolfcrypt/curve25519.h index 3646667e0c..b3e5b13a1b 100644 --- a/wolfssl/wolfcrypt/curve25519.h +++ b/wolfssl/wolfcrypt/curve25519.h @@ -61,11 +61,17 @@ typedef struct { * the mathematical functions used the endianness */ typedef struct { byte point[CURVE25519_KEYSIZE]; - #ifdef FREESCALE_LTC_ECC - byte pointY[CURVE25519_KEYSIZE]; - #endif +#ifdef FREESCALE_LTC_ECC + byte pointY[CURVE25519_KEYSIZE]; +#endif + byte pointSz; } ECPoint; +#ifndef WC_CURVE25519KEY_TYPE_DEFINED + typedef struct curve25519_key curve25519_key; + #define WC_CURVE25519KEY_TYPE_DEFINED +#endif + /* A CURVE25519 Key */ typedef struct curve25519_key { int idx; /* Index into the ecc_sets[] for the parameters of @@ -73,8 +79,8 @@ typedef struct curve25519_key { curve in dp */ const curve25519_set_type* dp; /* domain parameters, either points to curves (idx >= 0) or user supplied */ - ECPoint p; /* public key */ - ECPoint k; /* private key */ + ECPoint p; /* public point for key */ + byte k[CURVE25519_KEYSIZE]; /* private scaler for key */ #ifdef WOLFSSL_ASYNC_CRYPT WC_ASYNC_DEV asyncDev; @@ -82,6 +88,10 @@ typedef struct curve25519_key { #if defined(WOLF_CRYPTO_CB) int devId; #endif + + /* bit fields */ + byte pubSet:1; + byte privSet:1; } curve25519_key; enum { diff --git a/wolfssl/wolfcrypt/curve448.h b/wolfssl/wolfcrypt/curve448.h index ba2059410f..83986fbe3c 100644 --- a/wolfssl/wolfcrypt/curve448.h +++ b/wolfssl/wolfcrypt/curve448.h @@ -43,6 +43,10 @@ #define CURVE448_KEY_SIZE 56 #define CURVE448_PUB_KEY_SIZE 56 +#ifndef WC_CURVE448KEY_TYPE_DEFINED + typedef struct curve448_key curve448_key; + #define WC_CURVE448KEY_TYPE_DEFINED +#endif /* A CURVE448 Key */ typedef struct curve448_key { @@ -52,6 +56,10 @@ typedef struct curve448_key { #ifdef WOLFSSL_ASYNC_CRYPT WC_ASYNC_DEV asyncDev; #endif + + /* bit fields */ + byte pubSet:1; + byte privSet:1; } curve448_key; enum { @@ -62,6 +70,10 @@ enum { WOLFSSL_API int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key); +WOLFSSL_API +int wc_curve448_make_pub(int public_size, byte* pub, int private_size, + const byte* priv); + WOLFSSL_API int wc_curve448_shared_secret(curve448_key* private_key, curve448_key* public_key, diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index ba83389290..40a6b7993d 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -180,6 +180,10 @@ enum { #endif }; +#endif /* HAVE_ECC */ + +#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \ + defined(HAVE_CURVE448) || defined(WOLFCRYPT_HAVE_SAKKE) /* Curve Types */ typedef enum ecc_curve_id { ECC_CURVE_INVALID = -1, @@ -227,7 +231,6 @@ typedef enum ecc_curve_id { #ifdef HAVE_CURVE448 ECC_X448, #endif - #ifdef WOLFCRYPT_HAVE_SAKKE ECC_SAKKE_1, #endif @@ -237,6 +240,9 @@ typedef enum ecc_curve_id { #endif ECC_CURVE_MAX } ecc_curve_id; +#endif + +#ifdef HAVE_ECC #ifdef HAVE_OID_ENCODING typedef word16 ecc_oid_t;