From 4db2ff872bffa278bf071ba302721625b21b08b9 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 13 Jul 2021 05:18:04 +0100 Subject: [PATCH] cose: keys and signing + validation Support for COSE keys and signing / validation - lws_cose_key_t and import / export / generation apis for EC / RSA / SYMMETRIC - cose_sign1 ES256/384/512,RS256/384/512 sign + validate, passes RFC8152 WG tests sign1-tests - cose_sign ES256/384/512,RS256/384/512 sign + validate, passes RFC8152 WG tests sign-tests - cose_mac0 HS256/HS256_64/384/512 sign + validate, passes RFC8152 WG tests hmac-examples - cose_mac HS256/HS256_64/384/512 validate, passes RFC8152 WG tests hmac-examples - lws-crypto-cose-key commandline tool for key / key set dumping and creation - lws-crypro-cose-sign commandline tool for signing / validation - lws-api-test-cose - large number of test vectors and tests from RFC8152 --- CMakeLists-implied-options.txt | 2 + CMakeLists.txt | 3 +- READMEs/README.cbor-cose.md | 272 +++ cmake/lws_config.h.in | 1 + include/libwebsockets.h | 4 +- include/libwebsockets/lws-cose.h | 511 +++++ include/libwebsockets/lws-gencrypto.h | 11 +- include/libwebsockets/lws-genec.h | 2 +- include/libwebsockets/lws-genrsa.h | 3 +- include/libwebsockets/lws-jwk.h | 4 +- lib/CMakeLists.txt | 4 + lib/core/private-lib-core.h | 9 + lib/cose/CMakeLists.txt | 39 + lib/cose/cose_key.c | 1188 +++++++++++ lib/cose/cose_sign.c | 536 +++++ lib/cose/cose_sign_alg.c | 271 +++ lib/cose/cose_validate.c | 1051 ++++++++++ lib/cose/cose_validate_alg.c | 274 +++ lib/cose/private-lib-cose.h | 148 ++ lib/jose/CMakeLists.txt | 3 +- lib/jose/jwk/jose_key.c | 649 ++++++ lib/jose/jwk/jwk.c | 651 +----- lib/jose/jws/jose.c | 8 +- lib/jose/private-lib-jose.h | 28 +- lib/tls/mbedtls/lws-genec.c | 4 +- lib/tls/mbedtls/lws-genrsa.c | 5 +- lib/tls/mbedtls/mbedtls-x509.c | 2 +- lib/tls/openssl/lws-genec.c | 23 +- lib/tls/openssl/lws-genhash.c | 4 + lib/tls/openssl/lws-genrsa.c | 6 +- .../api-tests/api-test-cose/CMakeLists.txt | 29 + .../api-tests/api-test-cose/README.md | 22 + .../api-tests/api-test-cose/keys.c | 931 +++++++++ .../api-tests/api-test-cose/main.c | 50 + .../api-tests/api-test-cose/sign.c | 1862 +++++++++++++++++ .../api-tests/api-test-lecp/main.c | 1 + .../minimal-crypto-cose-key/CMakeLists.txt | 55 + .../crypto/minimal-crypto-cose-key/README.md | 132 ++ .../crypto/minimal-crypto-cose-key/main.c | 313 +++ .../crypto/minimal-crypto-cose-key/set1.cks | Bin 0 -> 1342 bytes .../minimal-crypto-cose-key/sign1_pass01.sig | Bin 0 -> 98 bytes .../minimal-crypto-cose-key/sign1_pass02.sig | 1 + .../minimal-crypto-cose-key/sign1_pass03.sig | 1 + .../minimal-crypto-cose-key/sign_pass01.sig | 1 + .../minimal-crypto-cose-key/sign_pass02.sig | 1 + .../minimal-crypto-cose-key/sign_pass03.sig | 1 + .../minimal-crypto-cose-sign/CMakeLists.txt | 219 ++ .../crypto/minimal-crypto-cose-sign/README.md | 105 + .../crypto/minimal-crypto-cose-sign/main.c | 406 ++++ .../minimal-crypto-cose-sign/payload.txt | 1 + .../minimal-crypto-cose-sign/rsa-4096.ck | Bin 0 -> 1569 bytes .../crypto/minimal-crypto-cose-sign/set1.cks | Bin 0 -> 1342 bytes .../minimal-crypto-cose-sign/sign-rsa4096.sig | Bin 0 -> 550 bytes .../minimal-crypto-cose-sign/sign1_pass01.sig | Bin 0 -> 98 bytes .../minimal-crypto-cose-sign/sign1_pass02.sig | 1 + .../minimal-crypto-cose-sign/sign1_pass03.sig | 1 + .../minimal-crypto-cose-sign/sign_pass01.sig | 1 + .../minimal-crypto-cose-sign/sign_pass02.sig | 1 + .../minimal-crypto-cose-sign/sign_pass03.sig | 1 + scripts/client-ca/create-client-cert.sh | 3 +- scripts/client-ca/create-server-cert.sh | 1 + 61 files changed, 9185 insertions(+), 671 deletions(-) create mode 100644 READMEs/README.cbor-cose.md create mode 100644 include/libwebsockets/lws-cose.h create mode 100644 lib/cose/CMakeLists.txt create mode 100644 lib/cose/cose_key.c create mode 100644 lib/cose/cose_sign.c create mode 100644 lib/cose/cose_sign_alg.c create mode 100644 lib/cose/cose_validate.c create mode 100644 lib/cose/cose_validate_alg.c create mode 100644 lib/cose/private-lib-cose.h create mode 100644 lib/jose/jwk/jose_key.c create mode 100644 minimal-examples/api-tests/api-test-cose/CMakeLists.txt create mode 100644 minimal-examples/api-tests/api-test-cose/README.md create mode 100644 minimal-examples/api-tests/api-test-cose/keys.c create mode 100644 minimal-examples/api-tests/api-test-cose/main.c create mode 100644 minimal-examples/api-tests/api-test-cose/sign.c create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/CMakeLists.txt create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/README.md create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/main.c create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/set1.cks create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass01.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass02.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass03.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/sign_pass01.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/sign_pass02.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-key/sign_pass03.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/CMakeLists.txt create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/README.md create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/main.c create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/payload.txt create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/rsa-4096.ck create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/set1.cks create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/sign-rsa4096.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/sign1_pass01.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/sign1_pass02.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/sign1_pass03.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass01.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass02.sig create mode 100644 minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass03.sig diff --git a/CMakeLists-implied-options.txt b/CMakeLists-implied-options.txt index 1675c693c..9579b1a12 100644 --- a/CMakeLists-implied-options.txt +++ b/CMakeLists-implied-options.txt @@ -101,6 +101,8 @@ if(LWS_WITH_DISTRO_RECOMMENDED) set(LWS_WITH_PLUGINS_BUILTIN 1) # selfcontained set(LWS_ROLE_RAW_PROXY 1) # selfcontained set(LWS_WITH_GENCRYPTO 1) # selfcontained / tls + set(LWS_WITH_CBOR 1) # selfcontained + set(LWS_WITH_COSE 1) # selfcontained set(LWS_WITH_JOSE 1) # selfcontained set(LWS_WITH_STRUCT_JSON 1) # selfcontained set(LWS_WITH_STRUCT_SQLITE3 1) # sqlite3 diff --git a/CMakeLists.txt b/CMakeLists.txt index d92cd63c8..5aefab592 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,7 +251,8 @@ set(LWS_LOGGING_BITFIELD_CLEAR 0 CACHE STRING "Bitfield describing which log lev option(LWS_LOGS_TIMESTAMP "Timestamp at start of logs" ON) option(LWS_LOG_TAG_LIFECYCLE "Log tagged object lifecycle as NOTICE" ON) option(LWS_AVOID_SIGPIPE_IGN "Android 7+ reportedly needs this" OFF) -option(LWS_WITH_JOSE "JSON Web Signature / Encryption / Keys (RFC7515/6/) API" OFF) +option(LWS_WITH_JOSE "JOSE JSON Web Signature / Encryption / Keys (RFC7515/6/) API" OFF) +option(LWS_WITH_COSE "COSE CBOR Signature / Encryption / Keys (RFC8152) API" OFF) option(LWS_WITH_GENCRYPTO "Enable support for Generic Crypto apis independent of TLS backend" OFF) option(LWS_WITH_SELFTESTS "Selftests run at context creation" OFF) option(LWS_WITH_GCOV "Build with gcc gcov coverage instrumentation" OFF) diff --git a/READMEs/README.cbor-cose.md b/READMEs/README.cbor-cose.md new file mode 100644 index 000000000..baa9d7dff --- /dev/null +++ b/READMEs/README.cbor-cose.md @@ -0,0 +1,272 @@ +# RFC8152 COSE apis + +||| +|---|---|---| +|cmake| `LWS_WITH_COSE`| +|Header| ./include/libwebsockets/lws-cose.h| +|api-test| ./minimal-examples/api-tests/api-test-cose/| +|README| ./READMEs/README.cbor-cose.md + +COSE is the CBOR equivalent of the JOSE suite of crypto objects and operations. +You can represent public and private EC, RSA and SYMMETRIC keys, and sets of +keys of various types; import the logical keys to and from CBOR; and sign / +verify and encrypt / decrypt payloads using structured CBOR. Key generation is +also supported. + +|type|operations|algs| +|---|---|---| +|lws_cose_key_t|import, export, generation|EC / RSA / SYMMETRIC| +|cose_sign1|sign, validate|ES256/384/512, RS256/384/512| +|cose_sign|sign, validate|ES256/384/512, RS256/384/512| +|cose_mac0|sign, validate|HS256/HS256_64/384/512| +|cose_mac|validate only|HS256/HS256_64/384/512| + +The lws COSE support uses the lws gencrypto layer, which calls through to the +tls crypto library, and so works on both OpenSSL and mbedTLS the same. + +An increasing number of higher-level IETF specifications use COSE underneath. + +## cose_key and sets + +Lws provides an `lws_cose_key_t` object to contain a single key's metadata and +key material for EC, RSA and SYMMETRIC key types. + +There is a commandline tool wrapping the key dumping and generation apis +available at `./minimal-examples/crypto/lws-crypto-cose-key` + +### cose_key and sets import from CBOR and destroying + +``` +lws_cose_key_t * +lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb, + void *user, const uint8_t *in, size_t len); +void +lws_cose_key_destroy(lws_cose_key_t **ck); + +void +lws_cose_key_set_destroy(lws_dll2_owner_t *o); +``` + +To convert a single key, `pkey_set` should be NULL and the created key will be +returned, for a cose_key set, which is simply a CBOR array of cose_keys, it +should be a prepared (ie, zero'd down if nothing in it) lws_dll2_owner_t that +will contain the resulting list of `lws_cose_key_t` objects that were created. +In both cases the return is NULL if there was a fatal error and anything created +has been cleaned up, the return has no other meaning in the cose_key set case. + +`lws_cose_key_destroy()` destroys a single `lws_cose_key_t` and sets the +contents of the pointer to NULL, for cose_key sets you instead pass a pointer to +the owner object to `lws_cose_key_set_destroy()` to destroy all the keys in the +set in one step. + +cose_key has some confusions about type, kty and alg may be either ints, +representing well-known standardized key and alg types, or freeform strings. +We convert the well-known ints to their string representations at import, so +there can be no confusion later. + +### cose_key generation + +``` +lws_cose_key_t * +lws_cose_key_generate(struct lws_context *context, int cose_kty, int use_mask, + int bits, const char *curve, const char *kid); +``` + +This creates an `lws_cose_key_t`, generates a key (SYMMETRIC) or keypair into +it and returns a pointer to it. + +`cose_kty` is one of `LWSCOSE_WKKTV_OKP`, `LWSCOSE_WKKTV_EC2`, `LWSCOSE_WKKTV_RSA`, +or `LWSCOSE_WKKTV_SYMMETRIC`. `bits` is valid for RSA keys and for EC keys, +`curve` should be a well-known curve name, one of `P-256`, `P-384` and `P-521` +currently. `use_mask` is a bitfield made up of (1 << LWSCOSE_WKKO_...) set to +enable the usage on the key. + +### cose_key export to CBOR + +The export api uses the same CBOR write context as `lws_lec_printf()` uses to +emit the key into an output buffer. Like the CBOR output apis, it may return +`LWS_LECPCTX_RET_AGAIN` to indicate it filled the buffer and should be called +again to fill another buffer. `lws_lec_init()` should be used to prepare the +write context and `lws_lec_setbuf()` to reset the output buffer on subsequent +calls, exactly the same as the CBOR write apis. + +``` +enum lws_lec_pctx_ret +lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags); +``` + +`flags` may be 0 to only output the public key pieces, or `LWSJWKF_EXPORT_PRIVATE` +to output everything. + +## Signing and signature validation + +COSE specifies three kinds of signed object, `cose_sign1` which signs a payload +with a single algorithm and key, `cose_sign` which may sign a payload with +multiple algorithms and keys, and `countersign`. + +`cose_sign1` has the advantage it can be validated with a single pass through +the signed object; `cose_sign` unfortunately specifies the parameters of the +signatures after the payload and must be done with multiple passes through the +payload, for inline payloads, by caching it in heap. + +`cose_sign` and `cose_sign1` objects are supported by lws, Countersigned +objects are not yet supported. + +`cose_mac0` is supported using HMAC for signing and validation, `cose_mac` is +only supported for validation. + +There is a commandline tool wrapping the signing and validation apis +available at `./minimal-examples/crypto/lws-crypto-cose-sign` + +### Signature validation + +Signature validation does not have to be done synchronously, to facilitate this +first you create a validation context specifying the type (eg, `SIGTYPE_SINGLE`) +and a keyset of public keys the signature might use to validate (notice even a +single key is passed in an lws_dll2_owner_t keyset). + +Creation uses a public `lws_cose_validate_create_info_t` info struct + +``` +typedef struct lws_cose_validate_create_info { + struct lws_context *cx; + /**< REQUIRED: the lws context */ + lws_dll2_owner_t *keyset; + /**< REQUIRED: one or more cose_keys */ + + enum lws_cose_sig_types sigtype; + /**< 0 if a CBOR tag is in the sig, else one of SIGTYPE_MULTI, + * SIGTYPE_SINGLE, etc*/ + + lws_cose_validate_pay_cb_t pay_cb; + /**< optional: called back with unvalidated payload pieces */ + void *pay_opaque; + /**< optional: passed into pay_cb callback along with payload chunk */ + + lws_cose_sign_ext_pay_cb_t ext_cb; + /**< optional extra application data provision callback */ + void *ext_opaque; + /**< optional extra application data provision callback opaque */ + size_t ext_len; + /**< if we have extra app data, this must be set to the length of it */ +} lws_cose_validate_create_info_t; +``` + +``` +struct lws_cose_validate_context * +lws_cose_validate_create(const lws_cose_validate_create_info_t *info); + +void +lws_cose_validate_destroy(struct lws_cose_validate_context **cps); +``` + +after that as pieces of the signature CBOR become available, they can be +processed by the validation context + +``` +int +lws_cose_validate_chunk(struct lws_cose_validate_context *cps, + const uint8_t *in, size_t in_len, size_t *used_in); +``` + +The parsing of the signature yields a list of result objects indicating +information about each signature it encountered and whether it was validated or +not. The parsing itself only fails if there is an unrecoverable error, the +completion of parsing does not indicate validation, it may yield zero or more +result objects indicating the validation failed. + +``` +lws_dll2_owner_t * +lws_cose_validate_results(struct lws_cose_validate_context *cps); + +typedef struct { + lws_dll2_t list; + + const lws_cose_key_t *cose_key; + cose_param_t cose_alg; + + int result; /* 0 = validated */ + +} lws_cose_validate_res_t; +``` + +It's like this because for multiple signatures, we may only have keys for some +of them, and we may have different policies for validation that can only be +assessed as a whole, eg, we may inisit that signatures pass with specific +algorithms, or all signatures for specific keys must be present and pass. This +way user code can assess the situation after the signature parsing and make its +decision about overall validity according to its own policies. + +## Signing + +Signing is again done by creating a signing context using an info struct to pass +in the paramter (a `lws_cose_sign_create_info_t`). + +``` +#define LCSC_FL_ADD_CBOR_TAG (1 << 0) +#define LCSC_FL_ADD_CBOR_PREFER_MAC0 (1 << 1) + +typedef struct lws_cose_sign_create_info { + struct lws_context *cx; + /**< REQUIRED: the lws context */ + lws_dll2_owner_t *keyset; + /**< REQUIRED: one or more cose_keys */ + + lws_lec_pctx_t *lec; + /**< REQUIRED: the cbor output context to emit to, user must + * initialize with lws_lec_init() beforehand */ + + lws_cose_sign_ext_pay_cb_t ext_cb; + /**< optional extra application data provision callback */ + void *ext_opaque; + /**< optional extra application data provision callback opaque */ + size_t ext_len; + /**< if we have extra app data, this must be set to the length of it */ + + size_t inline_payload_len; + /**< REQUIRED: size of the inline payload we will provide */ + + int flags; + /**< bitmap of LCSC_FL_* */ + enum lws_cose_sig_types sigtype; + /**< 0, or sign type hint */ +} lws_cose_sign_create_info_t; +``` + +``` +struct lws_cose_sign_context * +lws_cose_sign_create(const lws_cose_sign_create_info_t *info); +``` + +After creating the signing context, you call `lws_cose_sign_add()` one or more +times to add algorithms and keys to sign with (since cose_sign allows multiple +recipients with the same payload signed in different ways). + +``` +int +lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg, + const lws_cose_key_t *ck); +``` + +The payload does not have to be provided all at once and can be passed in chunk +by chunk over time via `lws_cose_sign_payload_chunk()`. + +Output is mediated via an lws CBOR output context provided in the info at +creation-time, it's only emitted during the `lws_cose_sign_payload_chunk()` +phase. If it returns `LWS_LECPCTX_RET_AGAIN`, you must call that api again +after using the CBOR output context data and resetting its buffer by +`lws_lec_setbuf()`, so it can continue to output. + +``` +enum lws_lec_pctx_ret +lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, + const uint8_t *in, size_t in_len); +``` + +Finally the signing context is destroyed. + +``` +void +lws_cose_sign_destroy(struct lws_cose_sign_context **csc); +``` + diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index a5d3d9dd1..26431dcfd 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -142,6 +142,7 @@ #cmakedefine LWS_WITH_BORINGSSL #cmakedefine LWS_WITH_CGI #cmakedefine LWS_WITH_CONMON +#cmakedefine LWS_WITH_COSE #cmakedefine LWS_WITH_CUSTOM_HEADERS #cmakedefine LWS_WITH_DEPRECATED_LWS_DLL #cmakedefine LWS_WITH_DETAILED_LATENCY diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 37aec68da..759f3d61f 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -616,8 +616,11 @@ struct lws; #if defined(LWS_WITH_FILE_OPS) #include #endif +#include + #include #include +#include #include #include #include @@ -647,7 +650,6 @@ struct lws; #include #endif -#include #include #include #include diff --git a/include/libwebsockets/lws-cose.h b/include/libwebsockets/lws-cose.h new file mode 100644 index 000000000..1ea67915d --- /dev/null +++ b/include/libwebsockets/lws-cose.h @@ -0,0 +1,511 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** \defgroup cose COSE apis + * ##COSE related functions + * \ingroup lwsaoi + * + * COSE RFC 8152 relates to signed and encrypted CBOR + */ +//@{ + +enum { + /* RFC8152: Table 2: Common Header Parameters + * https://www.iana.org/assignments/cose/cose.xhtml#header-parameters + */ + + LWSCOSE_WKL_ALG = 1, /* int / tstr */ + LWSCOSE_WKL_CRIT, /* [+ label ] */ + LWSCOSE_WKL_CONTENT_TYPE, /* tstr / uint */ + LWSCOSE_WKL_KID, /* bstr */ + LWSCOSE_WKL_IV, /* bstr */ + LWSCOSE_WKL_IV_PARTIAL, /* bstr */ + LWSCOSE_WKL_COUNTERSIG, /* COSE sig(s) */ + LWSCOSE_WKL_COUNTERSIG0 = 9, /* bstr */ + LWSCOSE_WKL_KID_CONTEXT, /* bstr */ + LWSCOSE_WKL_CUPH_NONCE = 256, /* bstr */ + LWSCOSE_WKL_CUPH_OWNER_PUBKEY = 257, /* array */ + + /* RFC8152: Table 3: key map labels */ + + LWSCOSE_WKK_KTY = 1, /* int / tstr */ + LWSCOSE_WKK_KID, /* bstr */ + LWSCOSE_WKK_ALG, /* int / tstr */ + LWSCOSE_WKK_KEY_OPS, /* [ + (int / tstr) ] */ + LWSCOSE_WKK_BASE_IV, /* bstr */ + + /* RFC8152: Table 4: Key Operation Values */ + + LWSCOSE_WKKO_SIGN = 1, + LWSCOSE_WKKO_VERIFY, + LWSCOSE_WKKO_ENCRYPT, + LWSCOSE_WKKO_DECRYPT, + LWSCOSE_WKKO_WRAP_KEY, + LWSCOSE_WKKO_UNWRAP_KEY, + LWSCOSE_WKKO_DERIVE_KEY, + LWSCOSE_WKKO_DERIVE_BITS, + LWSCOSE_WKKO_MAC_CREATE, + LWSCOSE_WKKO_MAC_VERIFY, + + /* RFC8152: Table 5: ECDSA algs */ + + LWSCOSE_WKAECDSA_ALG_ES256 = -7, + LWSCOSE_WKAECDSA_ALG_ES384 = -35, + LWSCOSE_WKAECDSA_ALG_ES512 = -36, + + /* RFC8152: Table 6: EDDSA algs */ + + LWSCOSE_WKAEDDSA_ALG_EDDSA = -8, + + /* RFC8152: Table 7: HMAC algs */ + + LWSCOSE_WKAHMAC_256_64 = 4, + LWSCOSE_WKAHMAC_256_256, + LWSCOSE_WKAHMAC_384_384, + LWSCOSE_WKAHMAC_512_512, + + /* RFC8152: Table 8: AES algs */ + + LWSCOSE_WKAAES_128_64 = 14, + LWSCOSE_WKAAES_256_64, + LWSCOSE_WKAAES_128_128 = 25, + LWSCOSE_WKAAES_256_128, + + /* RFC8152: Table 9: AES GCM algs */ + + LWSCOSE_WKAAESGCM_128 = 1, + LWSCOSE_WKAAESGCM_192, + LWSCOSE_WKAAESGCM_256, + + /* RFC8152: Table 10: AES CCM algs */ + + LWSCOSE_WKAAESCCM_16_64_128 = 10, + LWSCOSE_WKAAESCCM_16_64_256, + LWSCOSE_WKAAESCCM_64_64_128, + LWSCOSE_WKAAESCCM_64_64_256, + LWSCOSE_WKAAESCCM_16_128_128, + LWSCOSE_WKAAESCCM_16_128_256, + LWSCOSE_WKAAESCCM_64_128_128, + LWSCOSE_WKAAESCCM_64_128_256, + + /* RFC8152: Table 11: CHACHA20 / Poly1305 */ + + LWSCOSE_WKACHACHA_POLY1305 = 24, + + /* RFC8152: Table 13: HKDF param */ + + LWSCOSE_WKAPHKDF_SALT = -20, + + /* RFC8152: Table 14: Context Algorithm Parameters */ + + LWSCOSE_WKAPCTX_PARTY_U_IDENTITY = -21, + LWSCOSE_WKAPCTX_PARTY_U_NONCE = -22, + LWSCOSE_WKAPCTX_PARTY_U_OTHER = -23, + LWSCOSE_WKAPCTX_PARTY_V_IDENTITY = -24, + LWSCOSE_WKAPCTX_PARTY_V_NONCE = -25, + LWSCOSE_WKAPCTX_PARTY_V_OTHER = -26, + + /* RFC8152: Table 15: Direct key */ + + LWSCOSE_WKK_DIRECT_CEK = -6, + + /* RFC8152: Table 16: Direct key with KDF */ + + LWSCOSE_WKK_DIRECT_HKDF_SHA_256 = -10, + LWSCOSE_WKK_DIRECT_HKDF_SHA_512 = -11, + LWSCOSE_WKK_DIRECT_HKDF_AES_128 = -12, + LWSCOSE_WKK_DIRECT_HKDF_AES_256 = -13, + + /* RFC8152: Table 17: AES Key Wrap Algorithm Values */ + + LWSCOSE_WKK_DIRECT_HKDFKW_SHA_256 = -3, + LWSCOSE_WKK_DIRECT_HKDFKW_SHA_512 = -4, + LWSCOSE_WKK_DIRECT_HKDFKW_AES_128 = -5, + + /* RFC8152: Table 18: ECDH Algorithm Values */ + + LWSCOSE_WKAECDH_ALG_ES_HKDF_256 = -25, + LWSCOSE_WKAECDH_ALG_ES_HKDF_512 = -26, + LWSCOSE_WKAECDH_ALG_SS_HKDF_256 = -27, + LWSCOSE_WKAECDH_ALG_SS_HKDF_512 = -28, + + /* RFC8152: Table 19: ECDH Algorithm Parameters */ + + LWSCOSE_WKAPECDH_EPHEMERAL_KEY = -1, + LWSCOSE_WKAPECDH_STATIC_KEY = -2, + LWSCOSE_WKAPECDH_STATIC_KEY_ID = -3, + + /* RFC8152: Table 20: ECDH Algorithm Parameters with key wrap */ + + LWSCOSE_WKAPECDH_ES_A128KW = -29, + LWSCOSE_WKAPECDH_ES_A192KW = -30, + LWSCOSE_WKAPECDH_ES_A256KW = -31, + LWSCOSE_WKAPECDH_SS_A128KW = -32, + LWSCOSE_WKAPECDH_SS_A192KW = -33, + LWSCOSE_WKAPECDH_SS_A256KW = -34, + + /* RFC8152: Table 21: Key Type Values + * https://www.iana.org/assignments/cose/cose.xhtml#key-type + */ + + LWSCOSE_WKKTV_OKP = 1, + LWSCOSE_WKKTV_EC2 = 2, + LWSCOSE_WKKTV_RSA = 3, + LWSCOSE_WKKTV_SYMMETRIC = 4, + LWSCOSE_WKKTV_HSS_LMS = 5, + LWSCOSE_WKKTV_WALNUTDSA = 6, + + + /* RFC8152: Table 22: Elliptic Curves + * https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves + */ + + LWSCOSE_WKEC_P256 = 1, + LWSCOSE_WKEC_P384, + LWSCOSE_WKEC_P521, + LWSCOSE_WKEC_X25519, + LWSCOSE_WKEC_X448, + LWSCOSE_WKEC_ED25519, + LWSCOSE_WKEC_ED448, + LWSCOSE_WKEC_SECP256K1, + + /* RFC8152: Table 23: EC Key Parameters */ + + LWSCOSE_WKECKP_CRV = -1, + LWSCOSE_WKECKP_X = -2, + LWSCOSE_WKECKP_Y = -3, + LWSCOSE_WKECKP_D = -4, + + /* RFC8152: Table 24: Octet Key Pair (OKP) Parameters */ + + LWSCOSE_WKOKP_CRV = -1, + LWSCOSE_WKOKP_X = -2, + LWSCOSE_WKOKP_D = -4, + + /* Additional from + * https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters + */ + + LWSCOSE_WKKPRSA_N = -1, + LWSCOSE_WKKPRSA_E = -2, + LWSCOSE_WKKPRSA_D = -3, + LWSCOSE_WKKPRSA_P = -4, + LWSCOSE_WKKPRSA_Q = -5, + LWSCOSE_WKKPRSA_DP = -6, + LWSCOSE_WKKPRSA_DQ = -7, + LWSCOSE_WKKPRSA_QINV = -8, + LWSCOSE_WKKPRSA_OTHER = -9, + LWSCOSE_WKKPRSA_RI = -10, + LWSCOSE_WKKPRSA_DI = -11, + LWSCOSE_WKKPRSA_TI = -12, + + /* RFC8152: Table 25: Symmetric Key Parameters */ + + LWSCOSE_WKSYMKP_KEY_VALUE = 4, + + /* RFC8152: Table 26: CoAP Content-Formats for COSE */ + + LWSCOAP_CONTENTFORMAT_COSE_SIGN = 98, + LWSCOAP_CONTENTFORMAT_COSE_SIGN1 = 18, + LWSCOAP_CONTENTFORMAT_COSE_ENCRYPT = 96, + LWSCOAP_CONTENTFORMAT_COSE_ENCRYPT0 = 16, + LWSCOAP_CONTENTFORMAT_COSE_MAC = 97, + LWSCOAP_CONTENTFORMAT_COSE_MAC0 = 17, + LWSCOAP_CONTENTFORMAT_COSE_KEY = 101, + LWSCOAP_CONTENTFORMAT_COSE_KEY_SET = 102, + + /* RFC8152: Table 27: Header Parameter for CounterSignature0 */ + + LWSCOSE_WKL_COUNTERSIGNATURE0 = 9, /* bstr */ + + /* RFC8812: Table 1: RSASSA-PKCS1-v1_5 Algorithm Values */ + + LWSCOSE_WKARSA_ALG_RS256 = -257, /* + SHA-256 */ + LWSCOSE_WKARSA_ALG_RS384 = -258, /* + SHA-384 */ + LWSCOSE_WKARSA_ALG_RS512 = -259, /* + SHA-512 */ +}; + +enum enum_cose_key_meta_tok { + COSEKEY_META_KTY, + COSEKEY_META_KID, + COSEKEY_META_KEY_OPS, + COSEKEY_META_BASE_IV, + COSEKEY_META_ALG, + + LWS_COUNT_COSE_KEY_ELEMENTS +}; + +typedef int64_t cose_param_t; + +LWS_VISIBLE LWS_EXTERN const char * +lws_cose_alg_to_name(cose_param_t alg); + +LWS_VISIBLE LWS_EXTERN cose_param_t +lws_cose_name_to_alg(const char *name); + +/* + * cose_key + */ + +typedef struct lws_cose_key { + /* key data elements */ + struct lws_gencrypto_keyelem e[LWS_GENCRYPTO_MAX_KEYEL_COUNT]; + /* generic meta key elements, like KID */ + struct lws_gencrypto_keyelem meta[LWS_COUNT_COSE_KEY_ELEMENTS]; + lws_dll2_t list; /* used when part of a set */ + int gencrypto_kty; /**< one of LWS_GENCRYPTO_KTY_ */ + cose_param_t kty; + cose_param_t cose_alg; + cose_param_t cose_curve; + char private_key; /* nonzero = has private key elements */ +} lws_cose_key_t; + +typedef int (*lws_cose_key_import_callback)(struct lws_cose_key *s, void *user); + +/** lws_cose_jwk_import() - Create an lws_cose_key_t object from cose_key CBOR + * + * \param pkey_set: NULL, or a pointer to an lws_dll2_owner_t for a cose_key set + * \param cb: callback for each jwk-processed key, or NULL if importing a single + * key with no parent "keys" JSON + * \param user: pointer to be passed to the callback, otherwise ignored by lws. + * NULL if importing a single key with no parent "keys" JSON + * \param in: a single cose_key + * \param len: the length of the cose_key in bytes + * + * Creates a single lws_cose_key_t if \p pkey_set is NULL or if the incoming + * CBOR doesn't start with an array, otherwise expects a CBOR array containing + * zero or more cose_key CBOR, and adds each to the \p pkey_set + * lws_dll2_owner_t struct. Created lws_cose_key_t are filled with data from + * the COSE representation and can be used with other COSE crypto ops. + */ +LWS_VISIBLE LWS_EXTERN lws_cose_key_t * +lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb, + void *user, const uint8_t *in, size_t len); + +/** lws_cose_key_export() - Create cose_key CBOR from an lws_cose_key_t + * + * \param ck: the lws_cose_key_t to export to CBOR + * \param ctx: the CBOR writing context (same as for lws_lec_printf()) + * \param flags: 0 to export only public elements, or LWSJWKF_EXPORT_PRIVATE + * + * Creates an lws_jwk struct filled with data from the COSE representation. + */ +LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret +lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags); + +/** + * lws_cose_key_generate() - generate a fresh key + * + * \param context: the lws_context used to get random + * \param cose_kty: one of LWSCOSE_WKKTV_ indicating the well-known key type + * \param use_mask: 0, or a bitfield where (1 << LWSCOSE_WKKO_...) set means valid for use + * \param bits: key bits for RSA + * \param curve: for EC keys, one of "P-256", "P-384" or "P-521" currently + * \param kid: string describing the key, or NULL + * + * Create an lws_cose_key_t of the specified type and return it + */ +LWS_VISIBLE LWS_EXTERN lws_cose_key_t * +lws_cose_key_generate(struct lws_context *context, cose_param_t cose_kty, + int use_mask, int bits, const char *curve, + const uint8_t *kid, size_t kl); + +LWS_VISIBLE LWS_EXTERN lws_cose_key_t * +lws_cose_key_from_set(lws_dll2_owner_t *set, const uint8_t *kid, size_t kl); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_key_destroy(lws_cose_key_t **ck); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_key_set_destroy(lws_dll2_owner_t *o); + +/* only available in _DEBUG build */ + +LWS_VISIBLE LWS_EXTERN void +lws_cose_key_dump(const lws_cose_key_t *ck); + +/* + * cose_sign + */ + +struct lws_cose_validate_context; + + +enum lws_cose_sig_types { + SIGTYPE_UNKNOWN, + SIGTYPE_MULTI, + SIGTYPE_SINGLE, + SIGTYPE_COUNTERSIGNED, /* not yet supported */ + SIGTYPE_MAC, /* only supported for validation */ + SIGTYPE_MAC0, +}; + +/* a list of these result objects is the output of the validation process */ + +typedef struct { + lws_dll2_t list; + + const lws_cose_key_t *cose_key; + cose_param_t cose_alg; + + int result; /* 0 = validated */ + +} lws_cose_validate_res_t; + +enum { + LCOSESIGEXTCB_RET_FINISHED, + LCOSESIGEXTCB_RET_AGAIN, + LCOSESIGEXTCB_RET_ERROR = -1 +}; + +typedef struct { + struct lws_cose_validate_context *cps; + const uint8_t *ext; + size_t xl; +} lws_cose_sig_ext_pay_t; + +typedef int (*lws_cose_sign_ext_pay_cb_t)(lws_cose_sig_ext_pay_t *x); +typedef int (*lws_cose_validate_pay_cb_t)(struct lws_cose_validate_context *cps, + void *opaque, const uint8_t *paychunk, + size_t paychunk_len); + +typedef struct lws_cose_validate_create_info { + struct lws_context *cx; + /**< REQUIRED: the lws context */ + lws_dll2_owner_t *keyset; + /**< REQUIRED: one or more cose_keys */ + + enum lws_cose_sig_types sigtype; + /**< 0 if a CBOR tag is in the sig, else one of SIGTYPE_MULTI, + * SIGTYPE_SINGLE, etc*/ + + lws_cose_validate_pay_cb_t pay_cb; + /**< optional: called back with unvalidated payload pieces */ + void *pay_opaque; + /**< optional: passed into pay_cb callback along with payload chunk */ + + lws_cose_sign_ext_pay_cb_t ext_cb; + /**< optional extra application data provision callback */ + void *ext_opaque; + /**< optional extra application data provision callback opaque */ + size_t ext_len; + /**< if we have extra app data, this must be set to the length of it */ +} lws_cose_validate_create_info_t; + +/** + * lws_cose_validate_create() - create a signature validation context + * + * \param info: struct describing the validation context to create + * + * Creates a signature validation context set up as described in \p info. + * + * You can then pass the signature cbor chunks to it using + * lws_cose_validate_chunk(), finialize and get the results list using + * lws_cose_validate_results() and destroy with lws_cose_validate_destroy(). + */ +LWS_VISIBLE LWS_EXTERN struct lws_cose_validate_context * +lws_cose_validate_create(const lws_cose_validate_create_info_t *info); + +/** + * lws_cose_validate_chunk() - passes chunks of CBOR into the signature validator + * + * \param cps: the validation context + * \param in: the chunk of CBOR (does not have to be logically complete) + * \param in_len: number of bytes available at \p in + * + * Parses signature CBOR to produce a list of result objects. + * + * + */ +LWS_VISIBLE LWS_EXTERN int +lws_cose_validate_chunk(struct lws_cose_validate_context *cps, + const uint8_t *in, size_t in_len, size_t *used_in); + +LWS_VISIBLE LWS_EXTERN lws_dll2_owner_t * +lws_cose_validate_results(struct lws_cose_validate_context *cps); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_validate_destroy(struct lws_cose_validate_context **cps); + +struct lws_cose_sign_context; + +#define LCSC_FL_ADD_CBOR_TAG (1 << 0) +#define LCSC_FL_ADD_CBOR_PREFER_MAC0 (1 << 1) + +typedef struct lws_cose_sign_create_info { + struct lws_context *cx; + /**< REQUIRED: the lws context */ + lws_dll2_owner_t *keyset; + /**< REQUIRED: one or more cose_keys */ + + lws_lec_pctx_t *lec; + /**< REQUIRED: the cbor output context to emit to, user must + * initialize with lws_lec_init() beforehand */ + + lws_cose_sign_ext_pay_cb_t ext_cb; + /**< optional extra application data provision callback */ + void *ext_opaque; + /**< optional extra application data provision callback opaque */ + size_t ext_len; + /**< if we have extra app data, this must be set to the length of it */ + + size_t inline_payload_len; + /**< REQUIRED: size of the inline payload we will provide */ + + int flags; + /**< bitmap of LCSC_FL_* */ + enum lws_cose_sig_types sigtype; + /**< 0, or sign type hint */ +} lws_cose_sign_create_info_t; + +/** + * lws_cose_sign_create() - Create a signing context + * + * \param info: a structure describing the signing context you want to create + * + * This allocates and returns a signing context created according to what is in + * the \p info parameter. + * + * \p info must be prepared with the lws_context, a keyset to use, a CBOR + * output context, and the inline payload length. + * + * Returns NULL on failure or the created signing context ready to add alg(s) + * to. + */ + +LWS_VISIBLE LWS_EXTERN struct lws_cose_sign_context * +lws_cose_sign_create(const lws_cose_sign_create_info_t *info); + +LWS_VISIBLE LWS_EXTERN int +lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg, + const lws_cose_key_t *ck); + +LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret +lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, + const uint8_t *in, size_t in_len); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_sign_destroy(struct lws_cose_sign_context **csc); + +//@} diff --git a/include/libwebsockets/lws-gencrypto.h b/include/libwebsockets/lws-gencrypto.h index 644ce892a..001823100 100644 --- a/include/libwebsockets/lws-gencrypto.h +++ b/include/libwebsockets/lws-gencrypto.h @@ -59,6 +59,13 @@ enum lws_gencrypto_rsa_tok { LWS_GENCRYPTO_RSA_KEYEL_DQ, LWS_GENCRYPTO_RSA_KEYEL_QI, + /* we don't actively use these if given, but may come from COSE */ + + LWS_GENCRYPTO_RSA_KEYEL_OTHER, + LWS_GENCRYPTO_RSA_KEYEL_RI, + LWS_GENCRYPTO_RSA_KEYEL_DI, + LWS_GENCRYPTO_RSA_KEYEL_TI, + LWS_GENCRYPTO_RSA_KEYEL_COUNT }; @@ -89,10 +96,10 @@ enum lws_gencrypto_aes_tok { * type. */ -struct lws_gencrypto_keyelem { +typedef struct lws_gencrypto_keyelem { uint8_t *buf; uint32_t len; -}; +} lws_gc_elem_t; /** diff --git a/include/libwebsockets/lws-genec.h b/include/libwebsockets/lws-genec.h index ee62abe3e..c18cc1e1f 100644 --- a/include/libwebsockets/lws-genec.h +++ b/include/libwebsockets/lws-genec.h @@ -147,7 +147,7 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name, */ LWS_VISIBLE LWS_EXTERN int lws_genecdsa_set_key(struct lws_genec_ctx *ctx, - struct lws_gencrypto_keyelem *el); + const struct lws_gencrypto_keyelem *el); /** lws_genecdsa_hash_sig_verify_jws() - Verifies a JWS ECDSA signature on a given hash * diff --git a/include/libwebsockets/lws-genrsa.h b/include/libwebsockets/lws-genrsa.h index 744a4843b..c9409463f 100644 --- a/include/libwebsockets/lws-genrsa.h +++ b/include/libwebsockets/lws-genrsa.h @@ -74,7 +74,8 @@ struct lws_genrsa_ctx { * This and related APIs operate identically with OpenSSL or mbedTLS backends. */ LWS_VISIBLE LWS_EXTERN int -lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el, +lws_genrsa_create(struct lws_genrsa_ctx *ctx, + const struct lws_gencrypto_keyelem *el, struct lws_context *context, enum enum_genrsa_mode mode, enum lws_genhash_types oaep_hashid); diff --git a/include/libwebsockets/lws-jwk.h b/include/libwebsockets/lws-jwk.h index ab4aff594..bf33b8041 100644 --- a/include/libwebsockets/lws-jwk.h +++ b/include/libwebsockets/lws-jwk.h @@ -52,7 +52,7 @@ struct lws_jwk { struct lws_gencrypto_keyelem e[LWS_GENCRYPTO_MAX_KEYEL_COUNT]; /* generic meta key elements, like KID */ struct lws_gencrypto_keyelem meta[LWS_COUNT_JWK_ELEMENTS]; - int kty; /**< one of LWS_JWK_ */ + int kty; /**< one of LWS_GENCRYPTO_KTY_ */ char private_key; /* nonzero = has private key elements */ }; @@ -64,6 +64,8 @@ struct lws_jwk_parse_state { lws_jwk_key_import_callback per_key_cb; void *user; int pos; + int cose_state; + int seen; unsigned short possible; }; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 45b04093e..5dec214a1 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -146,6 +146,10 @@ endif() if (LWS_WITH_JOSE) add_subdir_include_dirs(jose) endif() +if (LWS_WITH_COSE) + add_subdir_include_dirs(cose) +endif() + if (LWS_WITH_SECURE_STREAMS) add_subdir_include_dirs(secure-streams) diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index 459b923e6..51078a5ab 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -950,6 +950,15 @@ lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, lws_filepos_t *amount); +int +lws_lec_scratch(lws_lec_pctx_t *ctx); +void +lws_lec_signed(lws_lec_pctx_t *ctx, int64_t num); + +int +lws_cose_key_checks(const lws_cose_key_t *key, int64_t kty, int64_t alg, + int key_op, const char *crv); + void lws_msleep(unsigned int); void diff --git a/lib/cose/CMakeLists.txt b/lib/cose/CMakeLists.txt new file mode 100644 index 000000000..c96e3db3e --- /dev/null +++ b/lib/cose/CMakeLists.txt @@ -0,0 +1,39 @@ +# +# libwebsockets - small server side websockets and web server implementation +# +# Copyright (C) 2010 - 2020 Andy Green +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# + +if (LWS_WITH_COSE) + list(APPEND SOURCES + cose/cose_key.c + cose/cose_validate.c + cose/cose_validate_alg.c + cose/cose_sign.c + cose/cose_sign_alg.c + ) +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/lib/cose/cose_key.c b/lib/cose/cose_key.c new file mode 100644 index 000000000..66247cbcc --- /dev/null +++ b/lib/cose/cose_key.c @@ -0,0 +1,1188 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * cose_key code + */ + +#include "private-lib-core.h" +//#include "private-lib-jose.h" + +#define lwsl_cose lwsl_notice +#define lwsl_hexdump_cose lwsl_hexdump_notice + +// #define VERBOSE 1 + +struct lws_cose_key_parse_state { + struct lws_cose_key *ck; + /**< single key created here if pkey_set is NULL */ + char buf[(8192 / 8) + 1]; + /**< enough for 8Kb key, only needed during parse */ + lws_cose_key_import_callback per_key_cb; + lws_dll2_owner_t *pkey_set; + /**< if non-NULL, expects a [ key set ], else single key */ + void *user; + size_t pos; + int cose_state; + cose_param_t seen[16]; + int seen_count; + int gencrypto_eidx; + int meta_idx; + unsigned short possible; +}; + +/* + * A COSE key representation is a CBOR map with a specified structure. The + * keys are + * + * LWSCOSE_WKK_KTY MUST int / tstr + * LWSCOSE_WKK_KID OPT bstr + * LWSCOSE_WKK_ALG OPT int / tstr + * LWSCOSE_WKK_KEY_OPS OPT [ + (int / tstr) ] + * LWSCOSE_WKK_BASE_IV OPT bstr + */ + +#if defined(_DEBUG) + +static const char *meta_names[] = { + "kty", "kid", "use", "key_ops", "base_iv", "alg" +}; + +static const char *oct_names[] = { + "k" +}; + +static const char *rsa_names[] = { + "e", "n", "d", "p", "q", "dp", "dq", "qi", "other", "ri", "di", "ti" +}; + +static const char *ec_names[] = { + "crv", "x", "d", "y", +}; + +void +lws_cose_key_dump(const struct lws_cose_key *ck) +{ + const char **enames; + char hex[2048]; + int elems; + int n; + + (void)enames; + (void)meta_names; + + switch (ck->gencrypto_kty) { + + case LWS_GENCRYPTO_KTY_OCT: + elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT; + enames = oct_names; + break; + case LWS_GENCRYPTO_KTY_RSA: + elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT; + enames = rsa_names; + break; + case LWS_GENCRYPTO_KTY_EC: + elems = LWS_GENCRYPTO_EC_KEYEL_COUNT; + enames = ec_names; + break; + + default: + lwsl_err("%s: jwk %p: unknown type\n", __func__, ck); + + return; + } + + lwsl_cose("%s: cose_key %p, kty: %lld (gc %d)\n", __func__, ck, + (long long)ck->kty, ck->gencrypto_kty); + + for (n = 0; n < LWS_COUNT_COSE_KEY_ELEMENTS; n++) { + if (ck->meta[n].buf) { + lws_hex_from_byte_array(ck->meta[n].buf, ck->meta[n].len, + hex, sizeof(hex)); + lwsl_cose(" meta: %s: %s\n", meta_names[n], hex); + } + } + + for (n = 0; n < elems; n++) { + if (ck->e[n].buf) { + lws_hex_from_byte_array(ck->e[n].buf, ck->e[n].len, + hex, sizeof(hex)); + lwsl_cose(" e: %s: %s\n", enames[n], hex); + } + } +} +#endif + +static const char * const kty_strings[] = { NULL, + "OKP", "EC2", "RSA", "SYMMETRIC", "HSS_LMS", "WALNUTDSA" +}; + +int +lws_cose_key_checks(const lws_cose_key_t *key, int64_t kty, cose_param_t alg, + int key_op, const char *crv) +{ + const struct lws_gencrypto_keyelem *ke; + + /* + * we ourselves have to have a very clear idea what we need, even if + * matches are optional in the key itself + */ + assert(key); + assert(kty); + assert(alg); + assert(key_op); + assert((kty != LWSCOSE_WKKTV_OKP && kty != LWSCOSE_WKKTV_EC2) || crv); + + /* RFC8152 8.1: + * + * The 'kty' field MUST be present, and it MUST be '...'. + * + * But kty can come as an int or a string, but we convert well-known + * kty ints to the corresponding string representation at key import + */ + if (!kty || kty >= (int)LWS_ARRAY_SIZE(kty_strings)) { + /* we don't understand it */ + lwsl_notice("%s: unknown kty %d\n", __func__, (int)kty); + goto bail; + } + + ke = &key->meta[COSEKEY_META_KTY]; + if (ke->buf && (strlen(kty_strings[kty]) != ke->len || + memcmp(kty_strings[kty], ke->buf, ke->len))) { + lwsl_notice("%s: key is of wrong kty\n", __func__); + lwsl_hexdump_notice(ke->buf, ke->len); + goto bail; + } + + /* ... + * If the 'alg' field is present, it MUST match the ... signature + * algorithm being used. + * + * We attempt to convert key alg text representations to a well-known + * index, if we can't, then we don't know the alg anyway and should fail + * it + */ + + if (!key->cose_alg && key->meta[COSEKEY_META_ALG].buf) { + lwsl_notice("%s: alg fail 1\n", __func__); + goto bail; + } + + if (key->cose_alg && /* accept it being absent altogether */ + key->cose_alg != alg) { + lwsl_notice("%s: alg fail 2\n", __func__); + + goto bail; + } + + /* ... + * If the 'key_ops' field is present, it MUST include 'sign' / 'verify' + * when creating /verifying an ... signature. + */ + + ke = &key->meta[COSEKEY_META_KEY_OPS]; + if (ke->buf && ke->len) { + uint32_t n; + + for (n = 0; n < ke->len; n++) + if (ke->buf[n] == key_op) + break; + + if (n == ke->len) + goto bail; + } + + /* + * If it's related to EC, check there is a curve associated with the + * key, and check it is what we expect + */ + + if (kty == LWSCOSE_WKKTV_OKP || kty == LWSCOSE_WKKTV_EC2) { + ke = &key->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + + if (!ke->buf) + goto bail; + if (ke->len != strlen(crv)) + goto bail; + if (memcmp(ke->buf, crv, ke->len)) + goto bail; + } + + /* We're willing to use this key for this operation */ + + return 0; + +bail: + lwsl_notice("%s: key rejected\n", __func__); + + return 1; +} + + +static int +lws_ck_set_el(struct lws_gencrypto_keyelem *e, char *in, size_t len) +{ + e->buf = lws_malloc(len + 1, "ck"); + if (!e->buf) + return -1; + + memcpy(e->buf, in, len); + e->buf[len] = '\0'; + e->len = (uint32_t)len; + + return 0; +} + +static struct { + const char *curve; + cose_param_t cose_id; +} cose_curves[] = { + { "P-256", LWSCOSE_WKEC_P256 }, + { "P-384", LWSCOSE_WKEC_P384 }, + { "P-521", LWSCOSE_WKEC_P521 }, + { "X25519", LWSCOSE_WKEC_X25519 }, + { "X448", LWSCOSE_WKEC_X448 }, + { "ED25519", LWSCOSE_WKEC_ED25519 }, + { "ED448", LWSCOSE_WKEC_ED448 }, + { "SECP256K1", LWSCOSE_WKEC_SECP256K1 }, +}; + +/* 0 means failed */ + +static cose_param_t +lws_cose_curve_name_to_id(const char *curve) +{ + int n; + + for (n = 0; n < (int)LWS_ARRAY_SIZE(cose_curves); n++) + if (!strcmp(cose_curves[n].curve, curve)) + return cose_curves[n].cose_id; + + return 0; +} + +static const char * +lws_cose_curve_id_to_name(cose_param_t id) +{ + int n; + + for (n = 0; n < (int)LWS_ARRAY_SIZE(cose_curves); n++) + if (cose_curves[n].cose_id == id) + return cose_curves[n].curve; + + return 0; +} + +static const char * const wk_algs[] = { + "ES256", "ES384", "ES512" +}; +static signed char wk_alg_indexes[] = { + LWSCOSE_WKAECDSA_ALG_ES256, + LWSCOSE_WKAECDSA_ALG_ES384, + LWSCOSE_WKAECDSA_ALG_ES512, +}; + +static signed char +cb_cose_key(struct lecp_ctx *ctx, char reason) +{ + struct lws_cose_key_parse_state *cps = + (struct lws_cose_key_parse_state *)ctx->user; + struct lws_gencrypto_keyelem *ke = NULL; + const char *p; + int n; + +#if defined(VERBOSE) + lwsl_notice("%s: reason %d, path %s, ord %u, ppos %d\n", __func__, + reason & 0x3f, + ctx->path, ctx->st[ctx->sp - 1].ordinal, + ctx->pst[ctx->pst_sp].ppos); +#endif + + switch (reason) { + case LECPCB_OBJECT_START: + if (cps->ck) + break; + goto ak; + case LECPCB_ARRAY_ITEM_START: + if (cps->pkey_set && ctx->pst[ctx->pst_sp].ppos == 2) { + ak: + cps->ck = lws_zalloc(sizeof(*cps->ck), __func__); + if (!cps->ck) + goto bail; + cps->cose_state = 0; + cps->meta_idx = -1; + cps->gencrypto_eidx = -1; + cps->seen_count = 0; + + if (cps->pkey_set) + lws_dll2_add_tail(&cps->ck->list, cps->pkey_set); + } + break; + case LECPCB_ARRAY_ITEM_END: + if (cps->pkey_set && ctx->pst[ctx->pst_sp].ppos == 2) { + if (cps->per_key_cb) + cps->per_key_cb(cps->ck, cps->user); + } + break; + case LECPCB_TAG_START: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_KEY) { + lwsl_warn("%s: unexpected tag\n", __func__); + goto bail; + } + break; + + case LECPCB_VAL_NUM_INT: + case LECPCB_VAL_NUM_UINT: + if (!ctx->sp) { + lwsl_warn("%s: unexpected uint %d, ppos %d\n", + __func__, ctx->sp, ctx->pst[ctx->sp].ppos); + goto bail; + } + + if (!lecp_parse_map_is_key(ctx)) { + const char *kty_str; + + /* value part of map */ + + switch (cps->cose_state) { + case LWSCOSE_WKK_KTY: + assert(cps->ck); + cps->ck->kty = (int)ctx->item.u.u64; + + /* convert the cose key type to gencrypto one */ + switch (ctx->item.u.u64) { + case LWSCOSE_WKKTV_OKP: + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_EC; + kty_str = "OKP"; + break; + case LWSCOSE_WKKTV_EC2: + kty_str = "EC2"; + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_EC; + break; + case LWSCOSE_WKKTV_RSA: + kty_str = "RSA"; + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_RSA; + break; + case LWSCOSE_WKKTV_SYMMETRIC: + kty_str = "SYMMETRIC"; + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_OCT; + break; + // case LWSCOSE_WKKTV_HSS_LMS: + // case LWSCOSE_WKKTV_WALNUTDSA: + default: + lwsl_warn("%s: unknown kty\n", __func__); + goto bail; + } + + /* store the string version of the key type */ + + ke = &cps->ck->meta[COSEKEY_META_KTY]; + ke->len = (uint32_t)strlen(kty_str); + ke->buf = lws_malloc(ke->len + 1, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, kty_str, ke->len + 1); + break; + case LWSCOSE_WKK_ALG: + /* + * He can tie the key to a cose alg code + */ + cps->ck->cose_alg = (int)ctx->item.u.u64; + break; + case LWSCOSE_WKK_KEY_OPS: + if (!cps->pkey_set && + (ctx->pst[ctx->sp].ppos != 3 || + strcmp(ctx->path, ".[]"))) { + lwsl_warn("%s: unexpected kops\n", + __func__); + goto bail; + } + if (cps->pkey_set && + (ctx->pst[ctx->sp].ppos != 5 || + strcmp(ctx->path, "[].[]"))) { + lwsl_warn("%s: unexpected kops\n", + __func__); + goto bail; + } + break; + case LWSCOSE_WKOKP_CRV: + cps->ck->cose_curve = (int)ctx->item.u.u64; + p = lws_cose_curve_id_to_name(cps->ck->cose_curve); + if (p) { + ke = &cps->ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + ke->len = (uint32_t)strlen(p); + ke->buf = lws_malloc(ke->len + 1, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, p, ke->len); + ke->buf[ke->len] = '\0'; + } + break; + default: + lwsl_warn("%s: uint not allowed in state %d\n", + __func__, cps->cose_state); + /* int not allowed in this state */ + goto bail; + } + + cps->cose_state = 0; + break; + } + + /* key part of map pair */ + + /* + * Disallow any of these coming more than once + */ + cps->cose_state = (int)ctx->item.u.u64; + for (n = 0 ; n < cps->seen_count; n++) + if (cps->seen[n] == cps->cose_state) { + /* dupe */ + lwsl_warn("%s: duplicate map name %d\n", + __func__, cps->cose_state); + goto bail; + } + + if (cps->seen_count >= (int)LWS_ARRAY_SIZE(cps->seen)) + goto bail; + cps->seen[cps->seen_count++] = cps->cose_state; + + cps->meta_idx = -1; + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKK_KTY: + cps->meta_idx = COSEKEY_META_KTY; + break; + case LWSCOSE_WKK_KID: + cps->meta_idx = COSEKEY_META_KID; + break; + case LWSCOSE_WKK_ALG: + cps->meta_idx = COSEKEY_META_ALG; + break; + case LWSCOSE_WKK_KEY_OPS: + cps->meta_idx = COSEKEY_META_KEY_OPS; + break; + case LWSCOSE_WKK_BASE_IV: + cps->meta_idx = COSEKEY_META_BASE_IV; + break; + + default: + cps->gencrypto_eidx = -1; + + switch (cps->ck->kty) { + case LWSCOSE_WKKTV_OKP: + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKOKP_CRV: + cps->cose_state = LWSCOSE_WKOKP_CRV; + break; + case LWSCOSE_WKOKP_X: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_X; + break; + case LWSCOSE_WKOKP_D: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_D; + break; + default: + goto bail; + } + break; + case LWSCOSE_WKKTV_EC2: + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKECKP_CRV: + cps->cose_state = LWSCOSE_WKOKP_CRV; + break; + case LWSCOSE_WKECKP_X: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_X; + break; + case LWSCOSE_WKECKP_Y: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_Y; + break; + case LWSCOSE_WKECKP_D: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_D; + break; + default: + goto bail; + } + break; + case LWSCOSE_WKKTV_RSA: + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKKPRSA_N: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_N; + break; + case LWSCOSE_WKKPRSA_E: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_E; + break; + case LWSCOSE_WKKPRSA_D: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_D; + break; + case LWSCOSE_WKKPRSA_P: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_P; + break; + case LWSCOSE_WKKPRSA_Q: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_Q; + break; + case LWSCOSE_WKKPRSA_DP: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_DP; + break; + case LWSCOSE_WKKPRSA_DQ: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_DQ; + break; + case LWSCOSE_WKKPRSA_QINV: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_QI; + break; + case LWSCOSE_WKKPRSA_OTHER: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_OTHER; + break; + case LWSCOSE_WKKPRSA_RI: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_RI; + break; + case LWSCOSE_WKKPRSA_DI: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_DI; + break; + case LWSCOSE_WKKPRSA_TI: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_TI; + break; + default: + goto bail; + } + break; + case LWSCOSE_WKKTV_SYMMETRIC: + if (ctx->item.u.i64 != -1 && + ctx->item.u.u64 != LWSCOSE_WKSYMKP_KEY_VALUE) + goto bail; + + cps->gencrypto_eidx = LWS_GENCRYPTO_OCT_KEYEL_K; + break; + default: + lwsl_warn("%s: unknown kty\n", __func__); + goto bail; + } + break; + } + break; + + case LECPCB_VAL_BLOB_START: + if (!ctx->sp || !(ctx->st[ctx->sp - 1].ordinal & 1)) { + lwsl_warn("%s: unexpected blob\n", __func__); + goto bail; + } + + if (cps->cose_state == COSEKEY_META_KID) + break; + + /* + * Validate the association of the blob now, collect it into + * the temp buf in cps and then alloc and copy it into the + * related key element when it's at the end and the size known + */ + + cps->pos = 0; + if (cps->gencrypto_eidx >= 0) { + if (cps->ck->e[cps->gencrypto_eidx].buf) { + lwsl_warn("%s: e[%d] set twice %d\n", __func__, + cps->gencrypto_eidx, + cps->ck->e[cps->gencrypto_eidx].len); + /* key elements must only come at most once */ + goto bail; + } + break; + } + if (cps->meta_idx >= 0) + break; + + goto bail; + + case LECPCB_VAL_BLOB_CHUNK: + case LECPCB_VAL_BLOB_END: + if (cps->pos + ctx->npos > sizeof(cps->buf)) { + lwsl_warn("%s: oversize blob\n", __func__); + goto bail; + } + memcpy(cps->buf + cps->pos, ctx->buf, ctx->npos); + cps->pos += ctx->npos; + + if (reason == LECPCB_VAL_BLOB_CHUNK) + break; + + /* we have the key element data, let's make the ck element */ + if (cps->gencrypto_eidx >= 0) { + + if (cps->ck->e[cps->gencrypto_eidx].buf) + break; + + lws_ck_set_el(&cps->ck->e[cps->gencrypto_eidx], + (char *)cps->buf, cps->pos); + cps->gencrypto_eidx = -1; + break; + } + + + if (cps->meta_idx >= 0) { + lws_ck_set_el(&cps->ck->meta[cps->meta_idx], + (char *)cps->buf, cps->pos); + cps->meta_idx = -1; + } + cps->pos = 0; + break; + case LECPCB_VAL_STR_END: + if (cps->cose_state == LWSCOSE_WKOKP_CRV) { + cps->ck->cose_curve = lws_cose_curve_name_to_id(ctx->buf); + ke = &cps->ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + ke->len = ctx->npos; + ke->buf = lws_malloc(ctx->npos, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, ctx->buf, ctx->npos); + } + + if (!lecp_parse_map_is_key(ctx) && + cps->cose_state == LWSCOSE_WKK_ALG) { + size_t n; + + for (n = 0; n < LWS_ARRAY_SIZE(wk_algs); n++) + if (ctx->npos == strlen(wk_algs[n]) && + !memcmp(ctx->buf, wk_algs[n], ctx->npos)) { + cps->ck->cose_alg = wk_alg_indexes[n]; + break; + } + + if (n == LWS_ARRAY_SIZE(wk_algs)) + /* key is for an alg we don't understand */ + lwsl_warn("%s: key for unknown alg %.*s\n", + __func__, (int)ctx->npos, ctx->buf); + + ke = &cps->ck->meta[COSEKEY_META_ALG]; + ke->len = ctx->npos; + ke->buf = lws_malloc(ctx->npos, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, ctx->buf, ctx->npos); + } + + break; + } + + return 0; + +bail: + lwsl_warn("%s: bail\n", __func__); + lws_cose_key_destroy(&cps->ck); + + if (cps->pkey_set) { + lws_cose_key_set_destroy(cps->pkey_set); + cps->pkey_set = NULL; + } + + return -1; +} + +void +lws_cose_key_destroy_elements(struct lws_gencrypto_keyelem *el, int m) +{ + int n; + + if (!el) + return; + + for (n = 0; n < m; n++) + if (el[n].buf) { + /* wipe all key material when it goes out of scope */ + lws_explicit_bzero(el[n].buf, el[n].len); + lws_free_set_NULL(el[n].buf); + el[n].len = 0; + } +} + +void +lws_cose_key_destroy(struct lws_cose_key **pck) +{ + struct lws_cose_key *ck = *pck; + + if (!ck) + return; + + lws_dll2_remove(&ck->list); + + lws_cose_key_destroy_elements(ck->e, LWS_ARRAY_SIZE(ck->e)); + lws_cose_key_destroy_elements(ck->meta, LWS_ARRAY_SIZE(ck->meta)); + + lws_free_set_NULL(*pck); +} + +static int +lws_cose_key_set_memb_remove(struct lws_dll2 *d, void *user) +{ + lws_cose_key_t *ck = lws_container_of(d, lws_cose_key_t, list); + + lws_dll2_remove(d); + lws_cose_key_destroy(&ck); + + return 0; +} + +void +lws_cose_key_set_destroy(lws_dll2_owner_t *o) +{ + lws_dll2_foreach_safe(o, NULL, lws_cose_key_set_memb_remove); +} + +lws_cose_key_t * +lws_cose_key_from_set(lws_dll2_owner_t *set, const uint8_t *kid, size_t kl) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head(set)) { + lws_cose_key_t *ck = lws_container_of(p, lws_cose_key_t, list); + struct lws_gencrypto_keyelem *ke = &ck->meta[COSEKEY_META_KID]; + + if (!kid) /* always the first then */ + return ck; + + if (ke->buf && ke->len == (uint32_t)kl && + !memcmp(ke->buf, kid, ke->len)) + return ck; + + } lws_end_foreach_dll(p); + + return NULL; +} + +lws_cose_key_t * +lws_cose_key_generate(struct lws_context *context, cose_param_t cose_kty, + int use_mask, int bits, const char *curve, + const uint8_t *kid, size_t kl) +{ + struct lws_gencrypto_keyelem *ke; + lws_cose_key_t *ck; + size_t sn; + int n; + + ck = lws_zalloc(sizeof(*ck), __func__); + if (!ck) + return NULL; + + ck->kty = cose_kty; + ck->private_key = 1; + + if (use_mask & 0xfffe) { + int count = 0; + + for (n = 1; n < 15; n++) + if (use_mask & (1 << n)) + count++; + ke = &ck->meta[COSEKEY_META_KEY_OPS]; + ke->buf = lws_malloc((size_t)count, __func__); + if (!ke->buf) + goto fail; + ke->len = (uint32_t)count; + count = 0; + for (n = 1; n < 15; n++) + if (use_mask & (1 << n)) + ke->buf[count++] = (uint8_t)n; + } + + if (kid) { + ke = &ck->meta[COSEKEY_META_KID]; + ke->buf = lws_malloc(kl, __func__); + ke->len = (uint32_t)kl; + memcpy(ke->buf, kid, ke->len); + } + + switch (cose_kty) { + case LWSCOSE_WKKTV_RSA: + { + struct lws_genrsa_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + ck->gencrypto_kty = LWS_GENCRYPTO_KTY_RSA; + + lwsl_notice("%s: generating %d bit RSA key\n", + __func__, bits); + n = lws_genrsa_new_keypair(context, &ctx, + LGRSAM_PKCS1_1_5, + ck->e, bits); + lws_genrsa_destroy(&ctx); + if (n) { + lwsl_err("%s: problem generating RSA key\n", + __func__); + goto fail; + } + } + break; + case LWSCOSE_WKKTV_SYMMETRIC: + + ck->gencrypto_kty = LWS_GENCRYPTO_KTY_OCT; + sn = (unsigned int)lws_gencrypto_bits_to_bytes(bits); + ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; + ke->buf = lws_malloc(sn, "oct"); + if (!ke->buf) + goto fail; + ke->len = (uint32_t)sn; + if (lws_get_random(context, ke->buf, sn) != sn) { + lwsl_err("%s: problem getting random\n", __func__); + goto fail; + } + break; + + case LWSCOSE_WKKTV_OKP: + case LWSCOSE_WKKTV_EC2: + { + struct lws_genec_ctx ctx; + + ck->gencrypto_kty = LWS_GENCRYPTO_KTY_EC; + + if (!curve) { + lwsl_err("%s: must have a named curve\n", __func__); + + goto fail; + } + + if (lws_genecdsa_create(&ctx, context, NULL)) + goto fail; + + ctx.genec_alg = LEGENEC_ECDSA; + lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__, + curve); + + n = lws_genecdsa_new_keypair(&ctx, curve, ck->e); + lws_genec_destroy(&ctx); + if (n) { + lwsl_err("%s: problem generating ECDSA key\n", __func__); + goto fail; + } + /* trim the trailing NUL */ + ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len = (uint32_t)strlen(curve); + } + break; + + default: + lwsl_err("%s: unknown kty\n", __func__); + goto fail; + } + + return ck; + +fail: + lws_free_set_NULL(ck); + + return NULL; +} + +struct lws_cose_key * +lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb, + void *user, const uint8_t *in, size_t len) +{ + struct lws_cose_key_parse_state cps; + struct lecp_ctx ctx; + int m; + + memset(&cps, 0, sizeof(cps)); + + cps.per_key_cb = cb; + cps.user = user; + cps.pkey_set = pkey_set; + cps.gencrypto_eidx = -1; + + lecp_construct(&ctx, cb_cose_key, &cps, NULL, 0); + m = lecp_parse(&ctx, in, len); + lecp_destruct(&ctx); + + if (m < 0) { + lwsl_notice("%s: parse got %d\n", __func__, m); + if (cps.pkey_set) + lws_cose_key_set_destroy(cps.pkey_set); + + return NULL; + } + + switch (cps.ck->gencrypto_kty) { + case LWS_GENCRYPTO_KTY_UNKNOWN: + lwsl_notice("%s: missing or unknown ktys\n", __func__); + goto bail; + default: + break; + } + + return cps.ck; + +bail: + lws_cose_key_destroy(&cps.ck); + return NULL; +} + +/* gencrypto element orering -> cose key parameters */ + +static const signed char ckp[3][12] = { + { /* LWS_GENCRYPTO_KTY_OCT (1) */ + /* LWS_GENCRYPTO_OCT_KEYEL_K */ LWSCOSE_WKSYMKP_KEY_VALUE, + }, + { /* LWS_GENCRYPTO_KTY_RSA (2) */ + /* LWS_GENCRYPTO_RSA_KEYEL_E */ LWSCOSE_WKKPRSA_E, + /* LWS_GENCRYPTO_RSA_KEYEL_N */ LWSCOSE_WKKPRSA_N, + /* LWS_GENCRYPTO_RSA_KEYEL_D */ LWSCOSE_WKKPRSA_D, + /* LWS_GENCRYPTO_RSA_KEYEL_P */ LWSCOSE_WKKPRSA_P, + /* LWS_GENCRYPTO_RSA_KEYEL_Q */ LWSCOSE_WKKPRSA_Q, + /* LWS_GENCRYPTO_RSA_KEYEL_DP */ LWSCOSE_WKKPRSA_DP, + /* LWS_GENCRYPTO_RSA_KEYEL_DQ */ LWSCOSE_WKKPRSA_DQ, + /* LWS_GENCRYPTO_RSA_KEYEL_QT */ LWSCOSE_WKKPRSA_QINV, + /* LWS_GENCRYPTO_RSA_KEYEL_OTHER */ LWSCOSE_WKKPRSA_OTHER, + /* LWS_GENCRYPTO_RSA_KEYEL_RI */ LWSCOSE_WKKPRSA_RI, + /* LWS_GENCRYPTO_RSA_KEYEL_DI */ LWSCOSE_WKKPRSA_DI, + /* LWS_GENCRYPTO_RSA_KEYEL_TI */ LWSCOSE_WKKPRSA_TI, + }, + { /* LWS_GENCRYPTO_KTY_EC (3) */ + /* LWS_GENCRYPTO_EC_KEYEL_CRV */ LWSCOSE_WKECKP_CRV, + /* LWS_GENCRYPTO_EC_KEYEL_X */ LWSCOSE_WKECKP_X, + /* LWS_GENCRYPTO_EC_KEYEL_D */ LWSCOSE_WKECKP_D, + /* LWS_GENCRYPTO_EC_KEYEL_Y */ LWSCOSE_WKECKP_Y, + } +}; + +enum lws_lec_pctx_ret +lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags) +{ + cose_param_t pa = 0; + int n; + + if (!ctx->opaque[0]) { + + ctx->opaque[0] = 1; /* map pair count */ + ctx->opaque[1] = 1; /* element index */ + ctx->opaque[2] = 0; /* public mask */ + ctx->opaque[3] = 0; /* doing AGAIN */ + + switch (ck->gencrypto_kty) { + case LWS_GENCRYPTO_KTY_OCT: + /* nothing to differentiate */ + ctx->opaque[2] = 1 << LWS_GENCRYPTO_OCT_KEYEL_K; + break; + case LWS_GENCRYPTO_KTY_RSA: + ctx->opaque[2] = 1 << LWS_GENCRYPTO_RSA_KEYEL_E; + break; + case LWS_GENCRYPTO_KTY_EC: + ctx->opaque[2] = (1 << LWS_GENCRYPTO_EC_KEYEL_X) | + (1 << LWS_GENCRYPTO_EC_KEYEL_Y); + break; + default: + goto fail; + } + + if (flags & LWSJWKF_EXPORT_PRIVATE) + ctx->opaque[2] = 0xffff; + + /* + * We first need to find out how many CBOR map pairs we are + * planning to create, so we can set a fixed length map of the + * right size. + */ + + for (n = 0; n < (int)LWS_ARRAY_SIZE(ck->e); n++) + if ((ctx->opaque[2] & (1 << n)) && ck->e[n].buf) + ctx->opaque[0]++; + + /* + * We always issue kty, others may be + * + * KID / ALG / KEY_OPS / BASE_IV + */ + + if (ck->meta[COSEKEY_META_KID].buf) + ctx->opaque[0]++; + if (ck->meta[COSEKEY_META_ALG].buf) + ctx->opaque[0]++; + if (ck->meta[COSEKEY_META_KEY_OPS].buf) + ctx->opaque[0]++; + if (ck->meta[COSEKEY_META_BASE_IV].buf) + ctx->opaque[0]++; + + lws_lec_int(ctx, LWS_CBOR_MAJTYP_MAP, 0, (uint64_t)ctx->opaque[0]); + lws_lec_signed(ctx, LWSCOSE_WKK_KTY); + lws_lec_signed(ctx, (int64_t)ck->kty); + + if (ck->gencrypto_kty == LWS_GENCRYPTO_KTY_EC) { + struct lws_gencrypto_keyelem *ke = + &ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + + if (!ke->buf || + ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len > 10) { + lwsl_err("%s: no curve type\n", __func__); + goto fail; + } + + pa = lws_cose_curve_name_to_id((const char *)ke->buf); + lws_lec_signed(ctx, LWSCOSE_WKECKP_CRV); + if (pa) + lws_lec_signed(ctx, pa); + else + lws_lec_printf(ctx, "%.*s", + (int)ke->len, ke->buf); + } + + + ctx->opaque[1] = COSEKEY_META_KID; + } + + /* + * Start from the second key meta, then do any elements that are set + */ + + while (ctx->buf != ctx->end) { + struct lws_gencrypto_keyelem *ke = NULL; + int cose_key_param = 0; + + if (lws_lec_scratch(ctx)) + break; + + if (ctx->opaque[1] == LWS_ARRAY_SIZE(ck->e) + + LWS_COUNT_COSE_KEY_ELEMENTS) + break; + + if (ctx->opaque[1] >= LWS_COUNT_COSE_KEY_ELEMENTS) { + n = ctx->opaque[1] - LWS_COUNT_COSE_KEY_ELEMENTS; + + if (ck->gencrypto_kty != LWS_GENCRYPTO_KTY_EC || + n != LWS_GENCRYPTO_EC_KEYEL_CRV) { + /* we didn't already encode his curve */ + + if ((ctx->opaque[2] & (1 << n)) && + ck->e[n].buf && ck->e[n].len) { + ke = &ck->e[n]; + cose_key_param = ckp[ck->gencrypto_kty - 1][n]; + } + } + } else + + switch (ctx->opaque[1]) { + + case COSEKEY_META_KID: /* bstr */ + if (ck->meta[COSEKEY_META_KID].buf) { + ke = &ck->meta[COSEKEY_META_KID]; + cose_key_param = LWSCOSE_WKK_KID; + // lwsl_hexdump_notice(ke->buf, ke->len); + } + break; + + case COSEKEY_META_ALG: /* int, tstr */ + if (ck->meta[COSEKEY_META_ALG].buf) { + ke = &ck->meta[COSEKEY_META_ALG]; + cose_key_param = LWSCOSE_WKK_ALG; + } + break; + + case COSEKEY_META_KEY_OPS: /* [ int ] */ + if (!ck->meta[COSEKEY_META_KEY_OPS].buf) + break; + ke = &ck->meta[COSEKEY_META_KEY_OPS]; + + n = (int)ke->len; + if (n > 10) + n = 10; + + /* + * We copy this array into scratch by hand now we + * made sure it will fit, we will never need AGAIN + */ + + lws_lec_signed(ctx, LWSCOSE_WKK_KEY_OPS); + lws_lec_int(ctx, LWS_CBOR_MAJTYP_ARRAY, 0, (uint64_t)n); + memcpy(&ctx->scratch[ctx->scratch_len], ke->buf, + (size_t)n); + ctx->scratch_len = (uint8_t)(ctx->scratch_len + (uint8_t)n); + ke = NULL; + break; + + case COSEKEY_META_BASE_IV: /* bstr */ + if (ck->meta[COSEKEY_META_BASE_IV].buf) { + ke = &ck->meta[COSEKEY_META_BASE_IV]; + cose_key_param = LWSCOSE_WKK_BASE_IV; + } + break; + + default: + break; + } + + if (ke && ke->buf && ke->len) { + + if (!ctx->opaque[3]) + lws_lec_signed(ctx, cose_key_param); + + /* binary string or text string? */ + if (ctx->opaque[1] == COSEKEY_META_KID || + ctx->opaque[1] == COSEKEY_META_BASE_IV || + ctx->opaque[1] >= LWS_COUNT_COSE_KEY_ELEMENTS) + n = (int)lws_lec_printf(ctx, "%.*b", + (int)ke->len, ke->buf); + else + n = (int)lws_lec_printf(ctx, "%.*s", + (int)ke->len, ke->buf); + + switch (n) { + case LWS_LECPCTX_RET_AGAIN: + ctx->opaque[3] = 1; + /* dump what we have and come back */ + continue; + case LWS_LECPCTX_RET_FAIL: + goto fail; + case LWS_LECPCTX_RET_FINISHED: + break; + } + } + + /* move on if we finished that guy */ + ctx->opaque[1]++; + ctx->opaque[3] = 0; + } + + ctx->used = lws_ptr_diff_size_t(ctx->buf, ctx->start); + + if (ctx->buf == ctx->end || ctx->scratch_len) + return LWS_LECPCTX_RET_AGAIN; + + ctx->opaque[0] = 0; + + return LWS_LECPCTX_RET_FINISHED; + +fail: + lwsl_notice("%s: failed\n", __func__); + + ctx->opaque[0] = 0; + + return LWS_LECPCTX_RET_FAIL; +} diff --git a/lib/cose/cose_sign.c b/lib/cose/cose_sign.c new file mode 100644 index 000000000..d7ae64f3d --- /dev/null +++ b/lib/cose/cose_sign.c @@ -0,0 +1,536 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" +#include "private-lib-cose.h" + +struct lws_cose_sign_context * +lws_cose_sign_create(const lws_cose_sign_create_info_t *info) +{ + struct lws_cose_sign_context *csc; + + /* you have to have prepared a cbor output context for us to use */ + assert(info->lec); + /* you have to provide at least one key in a cose_keyset */ + assert(info->keyset); + /* you have to provide an lws_context (for crypto random) */ + assert(info->cx); + + if (info->sigtype == SIGTYPE_MAC) { + lwsl_err("%s: only mac0 supported for signing\n", __func__); + return NULL; + } + + csc = lws_zalloc(sizeof(*csc), __func__); + if (!csc) + return NULL; + + csc->info = *info; + + return csc; +} + +int +lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg, + const lws_cose_key_t *ck) +{ + lws_cose_sig_alg_t *si = lws_cose_sign_alg_create(csc->info.cx, ck, alg, + LWSCOSE_WKKO_SIGN); + + if (!si) + return 1; + + lws_dll2_add_tail(&si->list, &csc->algs); + + return 0; +} + +static signed char cose_tags[] = { + 0, + LWSCOAP_CONTENTFORMAT_COSE_SIGN, + LWSCOAP_CONTENTFORMAT_COSE_SIGN1, + LWSCOAP_CONTENTFORMAT_COSE_SIGN, + LWSCOAP_CONTENTFORMAT_COSE_MAC, + LWSCOAP_CONTENTFORMAT_COSE_MAC0 +}; + +static void +lws_cose_sign_hashing(struct lws_cose_sign_context *csc, + const uint8_t *in, size_t in_len) +{ + //lwsl_hexdump_warn(in, in_len); + + assert(in_len); + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&csc->algs)) { + lws_cose_sig_alg_t *alg = lws_container_of(p, + lws_cose_sig_alg_t, list); + + if (lws_cose_sign_alg_hash(alg, in, in_len)) + alg->failed = 1; + } lws_end_foreach_dll_safe(p, tp); +} + +/* + * These chunks may be payload or application AAD being emitted into the + * signed object somewhere else. But we do not emit them ourselves here + * (since other non-emitted things are also hashed by us) and so can always + * deal with the whole in_len in one step. + */ + +enum lws_lec_pctx_ret +lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, + const uint8_t *in, size_t in_len) +{ + uint8_t lbuf[MAX_BLOBBED_PARAMS], lb[9]; + const struct lws_gencrypto_keyelem *ke; + enum lws_lec_pctx_ret ret; + lws_lec_pctx_t lec, lec1; + lws_cose_sig_alg_t *alg; + uint8_t c; + size_t s; + + switch (csc->tli) { + case ST_UNKNOWN: + /* + * We need to figure out what signing structure we need to use, + * given the algorithms that are in it. So let's have a look + * and decide. + */ + + if (!csc->algs.count) { + lwsl_err("%s: must add at least one signature\n", __func__); + return 1; + } + + csc->type = SIGTYPE_MULTI; + alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); + + switch (alg->cose_alg) { + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: +// if (csc->info.sigtype == SIGTYPE_MAC0) + csc->type = SIGTYPE_MAC0; +// else +// csc->type = SIGTYPE_MAC; + break; + } + + if (csc->algs.count == 1) { + if (!csc->info.sigtype && csc->type == SIGTYPE_MAC) { + if (csc->info.flags & LCSC_FL_ADD_CBOR_PREFER_MAC0) + csc->type = SIGTYPE_MAC0; + } else + if (!csc->info.sigtype || + csc->info.sigtype == SIGTYPE_SINGLE) /* ie, if no hint */ + csc->type = SIGTYPE_SINGLE; + } + + lwsl_notice("%s: decided on type %d\n", __func__, csc->type); + + /* + * Start emitting the appropriate tag if that's requested + */ + + if (csc->info.flags & LCSC_FL_ADD_CBOR_TAG) { + ret = lws_lec_printf(csc->info.lec, "%t(", + cose_tags[csc->type]); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + } + + /* The */ + c = 0; + switch (csc->type) { + case SIGTYPE_MAC0: + case SIGTYPE_MULTI: + case SIGTYPE_SINGLE: + c = 0x84; + break; + case SIGTYPE_MAC: + c = 0x85; + break; + default: + break; + } + + /* The outer array */ + csc->info.lec->scratch[csc->info.lec->scratch_len++] = c; + + /* + * Then, let's start hashing with the sigtype constant part + */ + + lws_cose_sign_hashing(csc, sig_mctx[csc->type], + sig_mctx_len[csc->type]); + + csc->tli = ST_OUTER_PROTECTED; + csc->subsequent = 0; + + /* fallthru */ + + case ST_OUTER_PROTECTED: + + /* + * We need to list and emit any outer protected data as a map + * into its own buffer, then emit that into the output as a bstr + */ + + switch (csc->type) { + case SIGTYPE_SINGLE: + case SIGTYPE_MAC0: + alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + + /* we know it will fit */ + lws_lec_printf(&lec, "{1:%lld}", + (long long)alg->cose_alg); + lws_lec_scratch(&lec); + + if (!csc->subsequent) { + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + lec.used); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + lws_cose_sign_hashing(csc, lec.start, lec.used); + ret = lws_lec_printf(csc->info.lec, "%.*b", + (int)lec.used, lec.start); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + csc->subsequent = 1; + } + break; + case SIGTYPE_MAC: + case SIGTYPE_MULTI: + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + lws_lec_int(&lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_lec_scratch(&lec); + lec.used = lws_ptr_diff_size_t(lec.buf, lec.start); + lws_cose_sign_hashing(csc, lec.start, + lec.used); + break; + default: + lec.used = 0; + break; + } + + csc->tli = ST_OUTER_UNPROTECTED; + + /* fallthru */ + + case ST_OUTER_UNPROTECTED: + + /* + * We need to list and emit any outer unprotected data, as + * an inline cbor map + */ + + switch (csc->type) { + case SIGTYPE_SINGLE: + case SIGTYPE_MAC0: + alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + ke = &alg->cose_key->meta[COSEKEY_META_KID]; + if (ke->len) { + ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}", + LWSCOSE_WKL_KID, + (int)ke->len, ke->buf); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + } + /* hack for no extra data */ + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + break; + case SIGTYPE_MAC: + case SIGTYPE_MULTI: + + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); + + /* + * For cose-sign, we need to feed each sig alg its alg- + * specific protected data into the hash before letting + * all the hashes see the payload + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&csc->algs)) { + alg = lws_container_of(p, lws_cose_sig_alg_t, list); + + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + + /* we know it will fit */ + lws_lec_printf(&lec, "{1:%lld}", + (long long)alg->cose_alg); + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + lec.used); + + // lwsl_hexdump_warn(lec1.scratch, lec1.scratch_len); + // lwsl_hexdump_warn(lec.start, lec.used); + if (lws_cose_sign_alg_hash(alg, lec1.scratch, + lec1.scratch_len)) + alg->failed = 1; + if (lws_cose_sign_alg_hash(alg, lec.start, + lec.used)) + alg->failed = 1; + + } lws_end_foreach_dll_safe(p, tp); + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + + break; + default: + ret = lws_lec_printf(csc->info.lec, "{}"); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + break; + } + + csc->tli = ST_OUTER_PAYLOAD; + csc->subsequent = 0; + + /* Prepare the payload BSTR */ + + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, + csc->info.inline_payload_len); + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + csc->info.inline_payload_len); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + + lws_lec_scratch(csc->info.lec); + + csc->rem_pay = csc->info.inline_payload_len; + + /* fallthru */ + + case ST_OUTER_PAYLOAD: + + if (csc->along) { + in += csc->along; + in_len -= csc->along; + } + + lws_lec_scratch(csc->info.lec); + + if (csc->rem_pay) { + + lws_cose_sign_hashing(csc, in, in_len); + + /* + * in / in_len is the payload chunk + */ + + s = lws_ptr_diff_size_t(csc->info.lec->end, + csc->info.lec->buf); + if (s > (size_t)csc->rem_pay) + s = (size_t)csc->rem_pay; + if (s > in_len) + s = in_len; + + memcpy(csc->info.lec->buf, in, s); + csc->info.lec->buf += s; + csc->info.lec->used = lws_ptr_diff_size_t( + csc->info.lec->buf, + csc->info.lec->start); + csc->rem_pay -= s; + + csc->along = s; + + return LWS_LECPCTX_RET_AGAIN; + } + + /* finished with rem_pay */ + + if (csc->type == SIGTYPE_MULTI) { + + csc->alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + csc->tli = ST_INNER_PROTECTED; + goto inner_protected; + } + csc->tli = ST_OUTER_SIGN1_SIGNATURE; + csc->along = 0; + + /* fallthru */ + + case ST_OUTER_SIGN1_SIGNATURE: + + alg = lws_container_of(lws_dll2_get_head(&csc->algs), + lws_cose_sig_alg_t, list); + + if (!alg->completed) + lws_cose_sign_alg_complete(alg); + if (alg->failed) + return LWS_LECPCTX_RET_FAIL; + + ret = lws_lec_printf(csc->info.lec, "%.*b", + (int)alg->rhash_len, alg->rhash); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + + if (csc->type == SIGTYPE_MAC) { + csc->alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + csc->tli = ST_INNER_PROTECTED; + goto inner_protected; + } + + break; + + case ST_INNER_PROTECTED: +inner_protected: + + /* + * We need to list and emit any outer protected data as a map + * into its own buffer, then emit that into the output as a bstr + */ + + switch (csc->type) { + case SIGTYPE_MAC: + case SIGTYPE_MULTI: + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, 3); + + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, 3); + + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + + /* we know it will fit */ + lws_lec_printf(&lec, "{1:%lld}", + (long long)csc->alg->cose_alg); + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + lec.used); + lws_lec_printf(csc->info.lec, "{1:%lld}", + (long long)csc->alg->cose_alg); + break; + default: + lec.used = 0; + break; + } + + + csc->tli = ST_INNER_UNPROTECTED; + + /* fallthru */ + + case ST_INNER_UNPROTECTED: + + switch (csc->type) { + case SIGTYPE_MULTI: + alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + ke = &alg->cose_key->meta[COSEKEY_META_KID]; + if (ke->len) { + ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}", + LWSCOSE_WKL_KID, + (int)ke->len, ke->buf); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + } + break; + default: + ret = lws_lec_printf(csc->info.lec, "{}"); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + break; + } + + lws_cose_sign_alg_complete(csc->alg); + if (csc->alg->failed) + return LWS_LECPCTX_RET_FAIL; + csc->tli = ST_INNER_SIGNATURE; + + /* fallthru */ + + case ST_INNER_SIGNATURE: + + ret = lws_lec_printf(csc->info.lec, "%.*b", + (int)csc->alg->rhash_len, csc->alg->rhash); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + + if (csc->alg->list.next) { + csc->alg = (lws_cose_sig_alg_t *)csc->alg->list.next; + csc->tli = ST_INNER_PROTECTED; + } + break; + + } + + return 0; +} + +void +lws_cose_sign_destroy(struct lws_cose_sign_context **_csc) +{ + struct lws_cose_sign_context *csc = *_csc; + + if (!csc) + return; + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&csc->algs)) { + lws_cose_sig_alg_t *alg = lws_container_of(p, + lws_cose_sig_alg_t, list); + + lws_dll2_remove(p); + lws_cose_sign_alg_destroy(&alg); + } lws_end_foreach_dll_safe(p, tp); + + lws_free_set_NULL(*_csc); +} diff --git a/lib/cose/cose_sign_alg.c b/lib/cose/cose_sign_alg.c new file mode 100644 index 000000000..71d524548 --- /dev/null +++ b/lib/cose/cose_sign_alg.c @@ -0,0 +1,271 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" +#include "private-lib-cose.h" + +lws_cose_sig_alg_t * +lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, + cose_param_t cose_alg, int op) +{ + lws_cose_sig_alg_t *alg = lws_zalloc(sizeof(*alg), __func__); + const struct lws_gencrypto_keyelem *ke; + enum lws_genhmac_types ghm; + enum lws_genhash_types gh; + const char *crv; + + if (!alg) + return NULL; + + alg->cose_alg = cose_alg; + alg->cose_key = ck; + + switch (cose_alg) { + + /* ECDSA algs */ + + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + crv = "P-256"; + gh = LWS_GENHASH_TYPE_SHA256; + alg->keybits = 256; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + crv = "P-384"; + gh = LWS_GENHASH_TYPE_SHA384; + alg->keybits = 384; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + crv = "P-521"; + gh = LWS_GENHASH_TYPE_SHA512; + alg->keybits = 521; +ecdsa: + + /* the key is good for this? */ + + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_EC2, cose_alg, + op, crv)) + goto bail_ecdsa; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_ecdsa; + + if (lws_genecdsa_create(&alg->u.ecdsactx, cx, lws_ec_curves)) { + lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", + __func__); + goto bail_ecdsa1; + } + + if (lws_genecdsa_set_key(&alg->u.ecdsactx, ck->e)) { + lwsl_notice("%s: ec key import fail\n", __func__); + goto bail_ecdsa2; + } + + break; + + /* HMAC algs */ + + case LWSCOSE_WKAHMAC_256_64: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 64; + goto hmac; + case LWSCOSE_WKAHMAC_256_256: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 256; + goto hmac; + case LWSCOSE_WKAHMAC_384_384: + ghm = LWS_GENHMAC_TYPE_SHA384; + alg->keybits = 384; + goto hmac; + case LWSCOSE_WKAHMAC_512_512: + ghm = LWS_GENHMAC_TYPE_SHA512; + alg->keybits = 512; + +hmac: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_SYMMETRIC, + cose_alg, op, NULL)) + goto bail_hmac; + + ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; + if (lws_genhmac_init(&alg->u.hmacctx, ghm, ke->buf, ke->len)) + goto bail_hmac; + + break; + + /* RSASSA algs */ + + case LWSCOSE_WKARSA_ALG_RS256: + gh = LWS_GENHASH_TYPE_SHA256; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS384: + gh = LWS_GENHASH_TYPE_SHA384; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS512: + gh = LWS_GENHASH_TYPE_SHA512; + +rsassa: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_RSA, cose_alg, + op, NULL)) + goto bail_hmac; + + alg->keybits = (int)ck->e[LWS_GENCRYPTO_RSA_KEYEL_N].len * 8; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_hmac; + + if (lws_genrsa_create(&alg->u.rsactx, ck->e, cx, + LGRSAM_PKCS1_1_5, gh)) { + lwsl_notice("%s: lws_genrsa_create fail\n", __func__); + goto bail_hmac; + } + break; + + default: + lwsl_warn("%s: unsupported alg %lld\n", __func__, + (long long)cose_alg); + goto bail_hmac; + } + + return alg; + +bail_ecdsa2: + lws_genec_destroy(&alg->u.ecdsactx); +bail_ecdsa1: + lws_genhash_destroy(&alg->hash_ctx, NULL); +bail_ecdsa: + lws_free(alg); + + return NULL; + +bail_hmac: + lws_free(alg); + + return NULL; +} + +int +lws_cose_sign_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len) +{ +#if defined(VERBOSE) + lwsl_hexdump_warn(in, in_len); +#endif + + switch (alg->cose_alg) { + + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + return lws_genhmac_update(&alg->u.hmacctx, in, in_len); + } + + /* EC, rsa are just making the hash before signing */ + + return lws_genhash_update(&alg->hash_ctx, in, in_len); +} + +/* + * We fill up alg-> rhash and rhash_len with the results, and destroy the + * crypto pieces cleanly. Call lws_cose_sign_alg_destroy() afterwards to + * clean up the alg itself. + */ + +void +lws_cose_sign_alg_complete(lws_cose_sig_alg_t *alg) +{ + uint8_t digest[LWS_GENHASH_LARGEST]; + unsigned int bytes; + uint8_t htype; + size_t hs; + + if (alg->completed) + return; + + switch (alg->cose_alg) { + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + hs = lws_genhash_size(alg->hash_ctx.type); + bytes = (unsigned int)lws_gencrypto_bits_to_bytes(alg->keybits); + lws_genhash_destroy(&alg->hash_ctx, digest); + alg->rhash_len = 0; + lwsl_notice("alg keybits %d hs %d\n", (int)alg->keybits, (int)hs); + if (!alg->failed && + lws_genecdsa_hash_sign_jws(&alg->u.ecdsactx, digest, + alg->hash_ctx.type, + (int)alg->keybits, alg->rhash, + 2u * bytes) >= 0) + alg->rhash_len = (int)(2 * bytes); + else + alg->failed = 1; + + lws_genec_destroy(&alg->u.ecdsactx); + break; + + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + alg->rhash_len = (int)lws_genhmac_size(alg->u.hmacctx.type); + if (alg->cose_alg == LWSCOSE_WKAHMAC_256_64) + alg->rhash_len = 8; + + if (lws_genhmac_destroy(&alg->u.hmacctx, alg->rhash)) { + lwsl_err("%s: destroy failed\n", __func__); + break; + } + break; + + case LWSCOSE_WKARSA_ALG_RS256: + case LWSCOSE_WKARSA_ALG_RS384: + case LWSCOSE_WKARSA_ALG_RS512: + bytes = (unsigned int)lws_gencrypto_bits_to_bytes(alg->keybits); + htype = alg->hash_ctx.type; + + if (!lws_genhash_destroy(&alg->hash_ctx, digest) && + !alg->failed && + lws_genrsa_hash_sign(&alg->u.rsactx, digest, htype, + alg->rhash, bytes) >= 0) + alg->rhash_len = (int)bytes; + else + lwsl_err("%s: lws_genrsa_hash_sign\n", __func__); + + lws_genrsa_destroy(&alg->u.rsactx); + break; + + default: + break; + } + + alg->completed = 1; +} + +void +lws_cose_sign_alg_destroy(lws_cose_sig_alg_t **_alg) +{ + lws_dll2_remove(&(*_alg)->list); + lws_cose_sign_alg_complete(*_alg); + lws_free_set_NULL(*_alg); +} diff --git a/lib/cose/cose_validate.c b/lib/cose/cose_validate.c new file mode 100644 index 000000000..1b9f3f3e7 --- /dev/null +++ b/lib/cose/cose_validate.c @@ -0,0 +1,1051 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * cose_sign handling + * + * Validation: + * + * - we put all our pieces and results in an lwsac in the parse state object + * + * - we collect pieces needed for sig validation into lwsac elements + * + * - we go through each signature making discrete results in the lwsac for + * the user code to assess + */ + +#include "private-lib-core.h" +#include "private-lib-cose.h" + +const uint8_t *sig_mctx[] = { (uint8_t *)"", + (uint8_t *)"\x85\x69""Signature", + (uint8_t *)"\x84\x6a""Signature1", + (uint8_t *)"\x85\x6f""CounterSignature", + (uint8_t *)"\x84\x63""MAC", + (uint8_t *)"\x84\x64""MAC0", +}; +uint8_t sig_mctx_len[] = { 0, 11, 12, 17, 5, 6 }; + +struct alg_names { + const char *name; + cose_param_t alg; +} alg_names[] = { + { "ES256", LWSCOSE_WKAECDSA_ALG_ES256 }, + { "ES384", LWSCOSE_WKAECDSA_ALG_ES384 }, + { "ES512", LWSCOSE_WKAECDSA_ALG_ES512 }, + { "HS256_64", LWSCOSE_WKAHMAC_256_64 }, + { "HS256", LWSCOSE_WKAHMAC_256_256 }, + { "HS384", LWSCOSE_WKAHMAC_384_384 }, + { "HS512", LWSCOSE_WKAHMAC_512_512 }, + { "RS256", LWSCOSE_WKARSA_ALG_RS256 }, + { "RS384", LWSCOSE_WKARSA_ALG_RS384 }, + { "RS512", LWSCOSE_WKARSA_ALG_RS512 }, +}; + +/* + * The Sig_structure plaintext is new temp CBOR made up from pieces from the + * cose_sign, cose_signature, and payload in a specific order + * + * tstr context string + * bstr 0-len or protected body headers + * bstr (Missing for sign1) 0-len or protected signer headers + * bstr 0-len or protected application part + * bstr the payload + * + * We are getting CBOR with an optional outer tag and then an array of exactly + * 4 items in a fixed order + * + * [ + * protected headers: bstr containing a map (captured as CBOR in cps->ph[]) + * unprotected: map: for sign1, eg, the alg (!?), the kid + * payload: bstr + * if sign: signatures: [ cose_signature struct array, + * each is a 3-element array + * [ + * protected: bstr containing a map: (eg, the alg) (captured as CBOR) + * unprotected: map: (eg, the kid) + * signature: bstr + * ] + * if sign1: bstr containing signature + * ] + * + * The last signatures field may be an array of signatures, or a single + * cose_signature object for cose_sign1. + * + * For cose_sign1, we know the signature alg before the payload and can do it + * in a single pass. But for sign, we do not know the signature algs until + * after the payload, which is an unfortunate oversight in cose_sign, meaning we + * cannot hash the payload one or more ways in a single pass. + */ + +#if defined(VERBOSE) +const char *cose_sections[] = { + "ST_UNKNOWN", + + "ST_OUTER_PROTECTED", + "ST_OUTER_UNPROTECTED", + "ST_OUTER_PAYLOAD", + "ST_OUTER_SIGN1_SIGNATURE", + + "ST_OUTER_SIGN_SIGARRAY", + + "ST_OUTER_MACTAG", + + "ST_INNER_PROTECTED", + "ST_INNER_UNPROTECTED", + "ST_INNER_SIGNATURE", + + "ST_INNER_EXCESS", +}; +#endif + +const char * +lws_cose_alg_to_name(cose_param_t alg) +{ + size_t n; + + for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++) + if (alg_names[n].alg == alg) + return alg_names[n].name; + + return "unknown_alg"; +} + +cose_param_t +lws_cose_name_to_alg(const char *name) +{ + size_t n; + + for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++) + if (!strcmp(alg_names[n].name, name)) + return alg_names[n].alg; + + return 0; +} + +static size_t +bstr_len(uint8_t *t, size_t buflen, uint8_t opcode, uint64_t len) +{ + uint8_t *ot = t; + + if (buflen < 9) + return 0; + + if (len < 24) { + *t = (uint8_t)(opcode | len); + + return 1; + } + if (len < 256) { + *t++ = opcode | LWS_CBOR_1; + goto b; + } + if (len < 65536) { + *t++ = opcode | LWS_CBOR_2; + goto b1; + } + if (len < 0xffffffffu) { + *t++ = opcode | LWS_CBOR_4; + goto b2; + } + + *t++ = opcode | LWS_CBOR_8; + + *t++ = (uint8_t)(len >> 56); + *t++ = (uint8_t)(len >> 48); + *t++ = (uint8_t)(len >> 40); + *t++ = (uint8_t)(len >> 32); + +b2: + *t++ = (uint8_t)(len >> 24); + *t++ = (uint8_t)(len >> 16); +b1: + *t++ = (uint8_t)(len >> 8); +b: + *t++ = (uint8_t)len; + + return lws_ptr_diff_size_t(t, ot); +} + +static int +apply_external(struct lws_cose_validate_context *cps) +{ + lws_cose_sig_alg_t *alg; + uint8_t t[9]; + + alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + return 0; + + /* get the external payload first, if any indicated */ + + if (cps->info.ext_len) { + lws_cose_sig_ext_pay_t ex; + size_t s; + + s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR, + cps->info.ext_len); + if (lws_cose_val_alg_hash(alg, t, s)) + return 1; + + memset(&ex, 0, sizeof(ex)); + ex.cps = cps; + + do { + int n; + + ex.xl = 0; + n = cps->info.ext_cb(&ex); + + if (ex.xl && + lws_cose_val_alg_hash(alg, ex.ext, ex.xl)) + return 1; + + if (n == LCOSESIGEXTCB_RET_ERROR) + return 1; + + if (n == LCOSESIGEXTCB_RET_FINISHED) + break; + } while (1); + } + + return 0; +} + +static int +create_alg(struct lecp_ctx *ctx, struct lws_cose_validate_context *cps) +{ + lws_cose_validate_param_stack_t *sl = &cps->st[cps->sp], *sl0 = &cps->st[0]; + lws_cose_validate_res_t *res; + lws_cose_sig_alg_t *alg; + lws_cose_key_t *ck; + uint8_t *p; + size_t s; + + /* with sign1, we can hash the payload in a + * single pass */ + + ck = lws_cose_key_from_set(cps->info.keyset, sl->kid.buf, sl->kid.len); + if (!ck) { + lwsl_notice("%s: no key\n", __func__); + lwsl_hexdump_notice(sl->kid.buf, sl->kid.len); + goto no_key_or_alg; + } + + // lwsl_notice("%s: cps->alg %d\n", __func__, (int)cps->alg); + + alg = lws_cose_val_alg_create(cps->info.cx, ck, cps->st[0].alg, + LWSCOSE_WKKO_VERIFY); + if (!alg) { + lwsl_info("%s: no alg\n", __func__); + +no_key_or_alg: + /* + * We can't create the alg then, so we can't normally + * create a result object. Create one especially for this + * case and continue on + */ + + res = lws_zalloc(sizeof(*res), __func__); + if (res) { + res->result = -1001; + + lws_dll2_add_tail(&res->list, &cps->results); + } + + return 0; + } + + lws_dll2_add_tail(&alg->list, &cps->algs); + + /* + * Hash step 1: The first hash content depends on + * sign/sign1/csign/mac/mac0 constant bstr + */ + + if (lws_cose_val_alg_hash(alg, sig_mctx[cps->info.sigtype], + sig_mctx_len[cps->info.sigtype])) + goto bail; + + /* + * Hash step 2: A zero-length bstr, or a copy of the + * OUTER protected headers + * + * A zero-entry map alone becomes a zero- + * length bstr + */ + + if (sl0->ph_pos[0] < 2) { + /* nothing to speak of */ + sl0->ph[0][0] = LWS_CBOR_MAJTYP_BSTR; + p = &sl0->ph[0][0]; + s = 1; + } else { + if (sl0->ph_pos[0] < 24) { + sl0->ph[0][2] = (uint8_t) + (LWS_CBOR_MAJTYP_BSTR | sl0->ph_pos[0]); + p = &sl0->ph[0][2]; + s = (size_t)sl0->ph_pos[0] + 1; + } else { + sl0->ph[0][1] = LWS_CBOR_MAJTYP_BSTR | + LWS_CBOR_1; + sl0->ph[0][2] = (uint8_t)sl0->ph_pos[0]; + p = &sl0->ph[0][1]; + s = (size_t)sl0->ph_pos[0] + 2; + } + } + + if (lws_cose_val_alg_hash(alg, p, s)) + goto bail; + + /* + * Hash step 3: Protected signer headers (Elided for sign1) + */ + + if (cps->info.sigtype == SIGTYPE_MULTI) { + if (sl->ph_pos[2] < 2) { + /* nothing to speak of */ + sl->ph[2][0] = LWS_CBOR_MAJTYP_BSTR; + p = &sl->ph[2][0]; + s = 1; + } else { + if (sl->ph_pos[2] < 24) { + sl->ph[2][2] = (uint8_t) + (LWS_CBOR_MAJTYP_BSTR | sl->ph_pos[2]); + p = &sl->ph[2][2]; + s = (size_t)sl->ph_pos[2] + 1; + } else { + sl->ph[2][1] = LWS_CBOR_MAJTYP_BSTR | + LWS_CBOR_1; + sl->ph[2][2] = (uint8_t)sl->ph_pos[2]; + p = &sl->ph[2][1]; + s = (size_t)sl->ph_pos[2] + 2; + } + } + + if (lws_cose_val_alg_hash(alg, p, s)) + goto bail; + } + + /* Hash step 4: bstr for applictation protected pieces + * empty for now + */ + + if (!cps->info.ext_len) { /* ie, if no app data */ + uint8_t u = LWS_CBOR_MAJTYP_BSTR; + if (lws_cose_val_alg_hash(alg, &u, 1)) + goto bail; + } + + /* + * The final part is the payload in its own bstr, as + * we get it if sign1, else replayed from a cache in heap + */ + + if (cps->info.sigtype == SIGTYPE_SINGLE) + return 0; + + if (!cps->payload_stash) { + lwsl_notice("%s: no payload stash\n", __func__); + goto bail; + } + + apply_external(cps); + + if (lws_cose_val_alg_hash(alg, cps->payload_stash, cps->payload_pos)) + goto bail; +lwsl_notice("a %d\n", (int)cps->sig_agg_pos); + + lws_cose_val_alg_destroy(cps, &alg, (const uint8_t *)cps->sig_agg, + cps->sig_agg_pos); + + return 0; + +bail: + return 1; +} + +#if defined(VERBOSE) +static const char * const reason_names[] = { + "LECPCB_CONSTRUCTED", + "LECPCB_DESTRUCTED", + "LECPCB_START", + "LECPCB_COMPLETE", + "LECPCB_FAILED", + "LECPCB_PAIR_NAME", + "LECPCB_VAL_TRUE", + "LECPCB_VAL_FALSE", + "LECPCB_VAL_NULL", + "LECPCB_VAL_NUM_INT", + "LECPCB_VAL_RESERVED", /* float in lejp */ + "LECPCB_VAL_STR_START", + "LECPCB_VAL_STR_CHUNK", + "LECPCB_VAL_STR_END", + "LECPCB_ARRAY_START", + "LECPCB_ARRAY_END", + "LECPCB_OBJECT_START", + "LECPCB_OBJECT_END", + "LECPCB_TAG_START", + "LECPCB_TAG_END", + "LECPCB_VAL_NUM_UINT", + "LECPCB_VAL_UNDEFINED", + "LECPCB_VAL_FLOAT16", + "LECPCB_VAL_FLOAT32", + "LECPCB_VAL_FLOAT64", + "LECPCB_VAL_SIMPLE", + "LECPCB_VAL_BLOB_START", + "LECPCB_VAL_BLOB_CHUNK", + "LECPCB_VAL_BLOB_END", + "LECPCB_ARRAY_ITEM_START", + "LECPCB_ARRAY_ITEM_END", + "LECPCB_LITERAL_CBOR" +}; +#endif + +static int +ph_index(struct lws_cose_validate_context *cps) +{ + switch (cps->tli) { + case ST_OUTER_PROTECTED: + return 0; + case ST_OUTER_UNPROTECTED: + return 1; + case ST_INNER_PROTECTED: + return 2; + case ST_INNER_UNPROTECTED: + return 3; + } + + assert(0); + return 0; +} + +static signed char +cb_cose_sig(struct lecp_ctx *ctx, char reason) +{ + struct lws_cose_validate_context *cps = + (struct lws_cose_validate_context *)ctx->user; + lws_cose_validate_param_stack_t *sl; + struct lws_gencrypto_keyelem *ke; + lws_cose_sig_alg_t *alg; + uint8_t t[9]; + size_t s; + int hi; + +#if defined(VERBOSE) + lwsl_notice("%s: %s, tli %s, sub %d, ppos %d, sp %d\n", __func__, + reason_names[reason & 0x1f], cose_sections[cps->tli], + cps->sub, ctx->pst[ctx->pst_sp].ppos, cps->sp); +#endif + + switch (reason) { + case LECPCB_CONSTRUCTED: + break; + + case LECPCB_TAG_START: + + lwsl_notice("%s: tag sigtype %d\n", __func__, cps->info.sigtype); + + switch (cps->info.sigtype) { + default: + assert(0); + break; + case SIGTYPE_UNKNOWN: + /* it means use the tag value to set the type */ + switch (ctx->item.u.u64) { + case LWSCOAP_CONTENTFORMAT_COSE_SIGN: + cps->info.sigtype = SIGTYPE_MULTI; + break; + case LWSCOAP_CONTENTFORMAT_COSE_SIGN1: + cps->info.sigtype = SIGTYPE_SINGLE; + break; +// case LWSCOAP_CONTENTFORMAT_COSE_SIGN__: +// cps->info.sigtype = SIGTYPE_COUNTERSIGNED; +// break; + case LWSCOAP_CONTENTFORMAT_COSE_MAC0: + cps->info.sigtype = SIGTYPE_MAC0; + break; + case LWSCOAP_CONTENTFORMAT_COSE_MAC: + cps->info.sigtype = SIGTYPE_MAC; + break; + default: + goto unexpected_tag; + } + break; + case SIGTYPE_MULTI: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN) + goto unexpected_tag; + break; + case SIGTYPE_SINGLE: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN1) + goto unexpected_tag; + break; + case SIGTYPE_COUNTERSIGNED: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN) + goto unexpected_tag; + break; + case SIGTYPE_MAC0: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC0) + goto unexpected_tag; + break; + case SIGTYPE_MAC: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC) { +unexpected_tag: + lwsl_warn("%s: unexpected tag %d\n", __func__, + (int)ctx->item.u.u64); + goto bail; + } + break; + } + + cps->depth++; + break; + + case LECPCB_ARRAY_ITEM_START: + + if (cps->sub) + break; + + if (ctx->pst[ctx->pst_sp].ppos == 4 || + ctx->pst[ctx->pst_sp].ppos == 6) { + switch (cps->tli) { + case ST_INNER_UNPROTECTED: + case ST_INNER_PROTECTED: + hi = ph_index(cps); + sl = &cps->st[cps->sp]; + sl->ph_pos[hi] = 0; + lecp_parse_report_raw(ctx, 1); + break; + default: + break; + } + break; + } + + if (ctx->pst[ctx->pst_sp].ppos != 2) + break; + + switch (cps->tli) { + case ST_OUTER_UNPROTECTED: + case ST_OUTER_PROTECTED: + /* + * Holy type confusion, Batman... this is a CBOR bstr + * containing valid CBOR that must also be parsed as + * part of the containing array... we need to collect + * it anyway since it is part of the signing plaintext + * in bstr form, let's get it and then parse it at the + * END of the bstr. + */ + lecp_parse_report_raw(ctx, 1); + break; + + case ST_OUTER_PAYLOAD: + if (cps->info.sigtype != SIGTYPE_SINGLE) + break; + + if (create_alg(ctx, cps)) + goto bail; + + break; + + case ST_OUTER_SIGN_SIGARRAY: + cps->tli = ST_INNER_PROTECTED; + break; + } + break; + + case LECPCB_ARRAY_ITEM_END: + + if (cps->sub) + break; + + if (ctx->pst[ctx->pst_sp].ppos == 2) { + sl = &cps->st[cps->sp]; + switch (cps->tli) { + case ST_OUTER_UNPROTECTED: + break; + /* fallthru */ + case ST_OUTER_PROTECTED: + lecp_parse_report_raw(ctx, 0); + + hi = ph_index(cps); + + if (!sl->ph_pos[hi] || cps->sub) + break; + + cps->sub = 1; + s = (size_t)sl->ph_pos[hi]; + + if (lecp_parse_subtree(&cps->ctx, + sl->ph[hi] + 3, s) != + LECP_CONTINUE) + goto bail; + cps->sub = 0; + break; + + case ST_OUTER_PAYLOAD: + switch (cps->info.sigtype) { + case SIGTYPE_MULTI: + cps->tli = ST_OUTER_SIGN_SIGARRAY - 1; + break; + case SIGTYPE_MAC: + case SIGTYPE_MAC0: + cps->tli = ST_OUTER_MACTAG - 1; + break; + case SIGTYPE_COUNTERSIGNED: + break; + default: + break; + } + break; + + case ST_OUTER_SIGN1_SIGNATURE: + case ST_OUTER_MACTAG: + cps->sp++; + cps->tli = ST_INNER_PROTECTED - 1; + break; + + case ST_INNER_UNPROTECTED: + lwsl_notice("ST_INNER_UNPROTECTED end\n"); + break; + case ST_INNER_PROTECTED: + lwsl_notice("ST_INNER_PROTECTED end\n"); + break; + + case ST_INNER_EXCESS: + case ST_OUTER_SIGN_SIGARRAY: + cps->tli--; /* so no change */ + break; + } + if (!cps->sub) + cps->tli++; + } + + if (ctx->pst[ctx->pst_sp].ppos >= 4) { + uint8_t *p; + uint8_t u; + size_t s1; + + switch (cps->tli) { + case ST_INNER_UNPROTECTED: + case ST_INNER_PROTECTED: + + hi = ph_index(cps); + sl = &cps->st[cps->sp]; + p = sl->ph[hi] + 3; + lecp_parse_report_raw(ctx, 0); + + if (!sl->ph_pos[hi] || cps->sub) { + if (!cps->sub) + cps->tli++; + break; + } + + cps->sub = 1; + s = (size_t)sl->ph_pos[hi]; + + /* + * somehow the raw captures the + * initial BSTR container length, + * let's strip it + */ + + u = (*p) & LWS_CBOR_SUBMASK; + if (((*p) & LWS_CBOR_MAJTYP_MASK) == + LWS_CBOR_MAJTYP_BSTR) { + s1 = 1; + if (u == LWS_CBOR_1) + s1 = 2; + else if (u == LWS_CBOR_2) + s1 = 3; + else if (u == LWS_CBOR_4) + s1 = 5; + else if (u == LWS_CBOR_8) + s1 = 9; + + if (s1 > s) + goto bail; + + sl->ph_pos[hi] = (int) + (sl->ph_pos[hi] - (ssize_t)s1); + s = s - s1; + memmove(p, p + s1, s); + } + + if (lecp_parse_subtree(&cps->ctx, p, s) != + LECP_CONTINUE) + goto bail; + + cps->sub = 0; + + if (!cps->sub) + cps->tli++; + break; + + case ST_INNER_SIGNATURE: + if (cps->info.sigtype == SIGTYPE_MAC) { + // lwsl_err("Y: alg %d\n", (int)cps->alg); + if (create_alg(ctx, cps)) + goto bail; + } + cps->tli++; + break; + default: + break; + } + } + + break; + + case LECPCB_VAL_NUM_INT: + case LECPCB_VAL_NUM_UINT: + switch (cps->tli) { + case ST_INNER_PROTECTED: + case ST_INNER_UNPROTECTED: + case ST_INNER_SIGNATURE: + case ST_OUTER_PROTECTED: + case ST_OUTER_UNPROTECTED: + if (lecp_parse_map_is_key(ctx)) { + cps->map_key = ctx->item.u.i64; + // lwsl_notice("%s: key %d\n", __func__, (int)cps->map_key); + break; + } + + // lwsl_notice("%s: key %d val %d\n", __func__, (int)cps->map_key, (int)ctx->item.u.i64); + + if (cps->map_key == LWSCOSE_WKL_ALG) { + sl = &cps->st[cps->sp]; + cps->map_key = 0; + if (cps->tli == ST_INNER_PROTECTED || + cps->tli == ST_INNER_UNPROTECTED || + cps->tli == ST_INNER_SIGNATURE) { + sl->alg = ctx->item.u.i64; + if (!cps->st[0].alg) + cps->st[0].alg = sl->alg; + } else + sl->alg = ctx->item.u.i64; + break; + } + break; + } + break; + + case LECPCB_VAL_STR_END: + switch (cps->tli) { + case ST_OUTER_UNPROTECTED: + break; + } + break; + + case LECPCB_VAL_BLOB_START: + + lwsl_notice("%s: blob size %d\n", __func__, (int)ctx->item.u.u64); + + if (cps->tli == ST_OUTER_SIGN1_SIGNATURE || + cps->tli == ST_INNER_SIGNATURE) { + if (ctx->item.u.u64 > sizeof(cps->sig_agg)) + goto bail; + cps->sig_agg_pos = 0; + break; + } + + if (cps->tli != ST_OUTER_PAYLOAD) + break; + + if (apply_external(cps)) { + lwsl_notice("%s: ext\n", __func__); + goto bail; + } + + s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR, + ctx->item.u.u64); + + if (cps->info.sigtype == SIGTYPE_SINGLE) { + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + break; + if (lws_cose_val_alg_hash(alg, t, s)) { + lwsl_notice("%s: hash failed\n", __func__); + goto bail; + } + + break; + } + + cps->payload_stash_size = (size_t)(ctx->item.u.u64 + s); + cps->payload_stash = lws_malloc(cps->payload_stash_size, + __func__); + if (!cps->payload_stash) { + lwsl_notice("%s: oom\n", __func__); + goto bail; + } + + memcpy(cps->payload_stash, t, s); + cps->payload_pos = s; + + break; + + case LECPCB_VAL_BLOB_CHUNK: + switch (cps->tli) { + case ST_OUTER_PAYLOAD: + + if (cps->info.pay_cb && ctx->npos) + cps->info.pay_cb(cps, cps->info.pay_opaque, + (uint8_t *)ctx->buf, ctx->npos); + + if (cps->payload_stash) { + if (cps->payload_pos + ctx->npos > + cps->payload_stash_size) + goto bail; + memcpy(cps->payload_stash + cps->payload_pos, + ctx->buf, ctx->npos); + cps->payload_pos += ctx->npos; + break; + } + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + break; + if (ctx->npos && + lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf, + ctx->npos)) { + lwsl_notice("%s: chunk fail\n", __func__); + goto bail; + } + break; + case ST_INNER_SIGNATURE: + case ST_OUTER_SIGN1_SIGNATURE: + /* the sig is big compared to ctx->buf... we need to + * stash it then */ + memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, + ctx->npos); + cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos; + break; + } + break; + + case LECPCB_VAL_BLOB_END: + switch (cps->tli) { + + case ST_INNER_SIGNATURE: + if (cps->info.sigtype == SIGTYPE_MULTI) { + memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, + ctx->npos); + cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos; + // lwsl_err("Y: alg %d\n", (int)cps->alg); + if (create_alg(ctx, cps)) + goto bail; + break; + } + if (cps->info.sigtype != SIGTYPE_MAC) + break; + /* fallthru */ + case ST_OUTER_PROTECTED: + case ST_OUTER_UNPROTECTED: + case ST_INNER_PROTECTED: + case ST_INNER_UNPROTECTED: + if (cps->map_key == LWSCOSE_WKL_KID) { + sl = &cps->st[cps->sp]; + ke = &sl->kid; + if (ke->buf) + lws_free(ke->buf); + ke->buf = lws_malloc(ctx->npos, __func__); + if (!ke->buf) + goto bail; + ke->len = ctx->npos; + memcpy(ke->buf, ctx->buf, ctx->npos); + cps->map_key = 0; + } + break; + + case ST_OUTER_PAYLOAD: + if (cps->info.pay_cb && ctx->npos) + cps->info.pay_cb(cps, cps->info.pay_opaque, + (uint8_t *)ctx->buf, ctx->npos); + if (cps->payload_stash) { + if (cps->payload_pos + ctx->npos > + cps->payload_stash_size) + goto bail; + memcpy(cps->payload_stash + cps->payload_pos, + ctx->buf, ctx->npos); + cps->payload_pos += ctx->npos; + break; + } + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + break; + + if (ctx->npos && + lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf, + ctx->npos)) + goto bail; + break; + + case ST_OUTER_SIGN1_SIGNATURE: + if (cps->info.sigtype == SIGTYPE_MULTI) + break; + + memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, + ctx->npos); + cps->sig_agg_pos += ctx->npos; + + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + lwsl_notice("b\n"); + if (alg) + lws_cose_val_alg_destroy(cps, &alg, + cps->sig_agg, + cps->sig_agg_pos); + break; + + case ST_OUTER_MACTAG: + if (cps->mac_pos + ctx->npos > sizeof(cps->mac)) + goto bail; + memcpy(cps->mac + cps->mac_pos, ctx->buf, ctx->npos); + cps->mac_pos += ctx->npos; + + if (cps->info.sigtype == SIGTYPE_MAC0) { + if (create_alg(ctx, cps)) + goto bail; + } + + break; + } + break; + + case LECPCB_LITERAL_CBOR: + /* only used for protected headers */ + switch (cps->tli) { + case ST_INNER_PROTECTED: + case ST_OUTER_PROTECTED: + case ST_INNER_UNPROTECTED: + case ST_OUTER_UNPROTECTED: + sl = &cps->st[cps->sp]; + hi = ph_index(cps); + if (sl->ph_pos[hi] + 3 + ctx->cbor_pos > + (int)sizeof(sl->ph[hi]) - 3) + /* more protected cbor than we can handle */ + goto bail; + memcpy(sl->ph[hi] + 3 + sl->ph_pos[hi], ctx->cbor, + ctx->cbor_pos); + sl->ph_pos[hi] += ctx->cbor_pos; + break; + } + } + + return 0; + +bail: + + return -1; +} + +struct lws_cose_validate_context * +lws_cose_validate_create(const lws_cose_validate_create_info_t *info) +{ + struct lws_cose_validate_context *cps; + + /* you have to provide at least one key in a cose_keyset */ + assert(info->keyset); + /* you have to provide an lws_context (for crypto random) */ + assert(info->cx); + + cps = lws_zalloc(sizeof(*cps), __func__); + if (!cps) + return NULL; + + cps->info = *info; + cps->tli = ST_OUTER_PROTECTED; + + lecp_construct(&cps->ctx, cb_cose_sig, cps, NULL, 0); + + return cps; +} + +int +lws_cose_validate_chunk(struct lws_cose_validate_context *cps, + const uint8_t *in, size_t in_len, size_t *used_in) +{ + int n; + + n = lecp_parse(&cps->ctx, in, in_len); + if (used_in) + *used_in = cps->ctx.used_in; + + if (n == LECP_CONTINUE) + return LECP_CONTINUE; + + lecp_destruct(&cps->ctx); + + return n; +} + +lws_dll2_owner_t * +lws_cose_validate_results(struct lws_cose_validate_context *cps) +{ + return &cps->results; +} + +void +lws_cose_validate_destroy(struct lws_cose_validate_context **_cps) +{ + struct lws_cose_validate_context *cps = *_cps; + + if (!cps) + return; + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&cps->algs)) { + lws_cose_sig_alg_t *alg = lws_container_of(p, + lws_cose_sig_alg_t, list); + + lws_dll2_remove(p); + lws_cose_val_alg_destroy(cps, &alg, NULL, 0); + } lws_end_foreach_dll_safe(p, tp); + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&cps->results)) { + lws_cose_validate_res_t *res = lws_container_of(p, + lws_cose_validate_res_t, list); + + lws_dll2_remove(p); + lws_free(res); + } lws_end_foreach_dll_safe(p, tp); + + lws_free_set_NULL(cps->payload_stash); + + lwsac_free(&cps->ac); + + while (cps->sp >= 0) { + if (cps->st[cps->sp].kid.buf) + lws_free(cps->st[cps->sp].kid.buf); + cps->sp--; + } + + lws_free_set_NULL(*_cps); +} diff --git a/lib/cose/cose_validate_alg.c b/lib/cose/cose_validate_alg.c new file mode 100644 index 000000000..9f2e888e3 --- /dev/null +++ b/lib/cose/cose_validate_alg.c @@ -0,0 +1,274 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" +#include "private-lib-cose.h" + +lws_cose_sig_alg_t * +lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, + cose_param_t cose_alg, int op) +{ + lws_cose_sig_alg_t *alg = lws_zalloc(sizeof(*alg), __func__); + struct lws_gencrypto_keyelem *ke; + enum lws_genhmac_types ghm; + enum lws_genhash_types gh; + const char *crv; + + if (!alg) + return NULL; + + alg->cose_alg = cose_alg; + alg->cose_key = ck; + + switch (cose_alg) { + + /* ECDSA algs */ + + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + crv = "P-256"; + gh = LWS_GENHASH_TYPE_SHA256; + alg->keybits = 256; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + crv = "P-384"; + gh = LWS_GENHASH_TYPE_SHA384; + alg->keybits = 384; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + crv = "P-521"; + gh = LWS_GENHASH_TYPE_SHA512; + alg->keybits = 521; +ecdsa: + + /* the key is good for this? */ + + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_EC2, cose_alg, + op, crv)) + goto bail_ecdsa; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_ecdsa; + + if (lws_genecdsa_create(&alg->u.ecdsactx, cx, lws_ec_curves)) { + lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", + __func__); + goto bail_ecdsa1; + } + + if (lws_genecdsa_set_key(&alg->u.ecdsactx, ck->e)) { + lwsl_notice("%s: ec key import fail\n", __func__); + goto bail_ecdsa2; + } + + break; + + /* HMAC algs */ + + case LWSCOSE_WKAHMAC_256_64: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 64; + goto hmac; + case LWSCOSE_WKAHMAC_256_256: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 256; + goto hmac; + case LWSCOSE_WKAHMAC_384_384: + ghm = LWS_GENHMAC_TYPE_SHA384; + alg->keybits = 384; + goto hmac; + case LWSCOSE_WKAHMAC_512_512: + ghm = LWS_GENHMAC_TYPE_SHA512; + alg->keybits = 512; + +hmac: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_SYMMETRIC, + cose_alg, op, NULL)) + goto bail_hmac; + + ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; + if (lws_genhmac_init(&alg->u.hmacctx, ghm, ke->buf, ke->len)) + goto bail_hmac; + + break; + + /* RSASSA algs */ + + case LWSCOSE_WKARSA_ALG_RS256: + gh = LWS_GENHASH_TYPE_SHA256; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS384: + gh = LWS_GENHASH_TYPE_SHA384; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS512: + gh = LWS_GENHASH_TYPE_SHA512; + +rsassa: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_RSA, cose_alg, + op, NULL)) + goto bail_hmac; + alg->keybits = (int)ck->e[LWS_GENCRYPTO_RSA_KEYEL_N].len * 8; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_ecdsa; + + if (lws_genrsa_create(&alg->u.rsactx, ck->e, cx, + LGRSAM_PKCS1_1_5, gh)) { + lwsl_notice("%s: lws_genrsa_create fail\n", __func__); + goto bail_ecdsa1; + } + break; + + default: + lwsl_warn("%s: unsupported alg %lld\n", __func__, + (long long)cose_alg); + goto bail_hmac; + } + + return alg; + +bail_ecdsa2: + lws_genec_destroy(&alg->u.ecdsactx); +bail_ecdsa1: + lws_genhash_destroy(&alg->hash_ctx, NULL); +bail_ecdsa: + lws_free(alg); + + lwsl_notice("%s: failed\n", __func__); + + return NULL; + +bail_hmac: + lws_free(alg); + + return NULL; +} + +int +lws_cose_val_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len) +{ +#if defined(VERBOSE) + lwsl_hexdump_warn(in, in_len); +#endif + + switch (alg->cose_alg) { + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + return lws_genhmac_update(&alg->u.hmacctx, in, in_len); + } + + return lws_genhash_update(&alg->hash_ctx, in, in_len); +} + +void +lws_cose_val_alg_destroy(struct lws_cose_validate_context *cps, + lws_cose_sig_alg_t **_alg, const uint8_t *against, + size_t against_len) +{ + uint8_t digest[LWS_GENHASH_LARGEST]; + lws_cose_sig_alg_t *alg = *_alg; + lws_cose_validate_res_t *res; + size_t hs, shs; + int keybits; + uint8_t ht; + + lws_dll2_remove(&alg->list); + ht = alg->hash_ctx.type; + keybits = alg->keybits; + + res = lws_zalloc(sizeof(*res), __func__); + if (res) { + + res->cose_key = alg->cose_key; + res->cose_alg = alg->cose_alg; + res->result = -999; + + lws_dll2_add_tail(&res->list, &cps->results); + } + + switch (alg->cose_alg) { + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + hs = lws_genhash_size(alg->hash_ctx.type); + lws_genhash_destroy(&alg->hash_ctx, digest); + + lwsl_notice("%d %d %d\n", (int)hs, (int)keybits, (int)against_len); + + if (res && against) + res->result = lws_genecdsa_hash_sig_verify_jws( + &alg->u.ecdsactx, digest, ht, + keybits, against, against_len); + lws_genec_destroy(&alg->u.ecdsactx); + break; + + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + shs = hs = lws_genhmac_size(alg->u.hmacctx.type); + if (alg->cose_alg == LWSCOSE_WKAHMAC_256_64) + shs = 8; + + if (lws_genhmac_destroy(&alg->u.hmacctx, digest)) { + lwsl_err("%s: destroy failed\n", __func__); + break; + } + + if (cps->mac_pos != shs) { + lwsl_warn("%s: mac wrong size\n", __func__); + /* we can't compare it, leave it at fail */ + break; + } + if (res && against) { + res->result = lws_timingsafe_bcmp(digest, cps->mac, + (uint32_t)shs); + if (res->result) + lwsl_warn("%s: hash mismatch\n", __func__); + } + break; + + case LWSCOSE_WKARSA_ALG_RS256: + case LWSCOSE_WKARSA_ALG_RS384: + case LWSCOSE_WKARSA_ALG_RS512: + + if (!lws_genhash_destroy(&alg->hash_ctx, digest) && + !alg->failed && + lws_genrsa_hash_sig_verify(&alg->u.rsactx, digest, + alg->hash_ctx.type, + against, against_len) >= 0) { + if (res) + res->result = 0; + } else + lwsl_err("%s: lws_genrsa_hash_verify\n", __func__); + + lws_genrsa_destroy(&alg->u.rsactx); + break; + } + + lws_free_set_NULL(*_alg); +} diff --git a/lib/cose/private-lib-cose.h b/lib/cose/private-lib-cose.h new file mode 100644 index 000000000..97cc26566 --- /dev/null +++ b/lib/cose/private-lib-cose.h @@ -0,0 +1,148 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#define VERBOSE + +#define MAX_BLOBBED_PARAMS 96 /* largest bstr-encoded params */ + +enum { + ST_UNKNOWN, + + ST_OUTER_PROTECTED, + ST_OUTER_UNPROTECTED, + ST_OUTER_PAYLOAD, + ST_OUTER_SIGN1_SIGNATURE, + + ST_OUTER_SIGN_SIGARRAY, + + ST_OUTER_MACTAG, + + ST_INNER_PROTECTED, + ST_INNER_UNPROTECTED, + ST_INNER_SIGNATURE, + + ST_INNER_EXCESS, +}; + +typedef struct lws_cose_sig_alg { + lws_dll2_t list; + uint8_t rhash[512]; + const lws_cose_key_t *cose_key; + struct lws_genhash_ctx hash_ctx; + union { + struct lws_genec_ctx ecdsactx; + struct lws_genrsa_ctx rsactx; + struct lws_genhmac_ctx hmacctx; + } u; + cose_param_t cose_alg; + int keybits; + int rhash_len; + + char failed; + char completed; +} lws_cose_sig_alg_t; + +typedef struct lws_cose_validate_param_stack { + uint8_t ph[4][MAX_BLOBBED_PARAMS]; + int ph_pos[4]; + struct lws_gencrypto_keyelem kid; + cose_param_t alg; +} lws_cose_validate_param_stack_t; + +struct lws_cose_validate_context { + lws_cose_validate_create_info_t info; + uint8_t mac[LWS_GENHASH_LARGEST]; + uint8_t sig_agg[512]; + lws_cose_validate_param_stack_t st[3]; + lws_dll2_owner_t algs; + lws_dll2_owner_t results; + uint8_t *payload_stash; + struct lwsac *ac; + struct lecp_ctx ctx; + void *user; + + size_t payload_pos; + size_t payload_stash_size; + + int seen; + int depth; + + int outer; + size_t mac_pos; + size_t sig_agg_pos; + + cose_param_t map_key; /* parsing temp before val */ + + int tli; /* toplevel item */ + int sp; + + uint8_t sub; +}; + +struct lws_cose_sign_context { + lws_cose_sign_create_info_t info; + + lws_dll2_owner_t algs; + lws_cose_sig_alg_t *alg; + + size_t rem_pay; + enum lws_cose_sig_types type; /* computed */ + int flags; + + size_t along; + + int tli; + + char subsequent; +}; + +extern const uint8_t *sig_mctx[]; +extern uint8_t sig_mctx_len[]; +extern const char *cose_sections[]; + +lws_cose_sig_alg_t * +lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, + cose_param_t cose_alg, int op); + +int +lws_cose_val_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len); + +void +lws_cose_val_alg_destroy(struct lws_cose_validate_context *cps, + lws_cose_sig_alg_t **_alg, const uint8_t *against, + size_t against_len); + +lws_cose_sig_alg_t * +lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, + cose_param_t cose_alg, int op); + +int +lws_cose_sign_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len); + +void +lws_cose_sign_alg_complete(lws_cose_sig_alg_t *alg); + +void +lws_cose_sign_alg_destroy(lws_cose_sig_alg_t **_alg); + diff --git a/lib/jose/CMakeLists.txt b/lib/jose/CMakeLists.txt index 2634f52c7..161744ef7 100644 --- a/lib/jose/CMakeLists.txt +++ b/lib/jose/CMakeLists.txt @@ -26,8 +26,9 @@ include_directories(. ./jwe ./jws ./jwk) if (LWS_WITH_JOSE) list(APPEND SOURCES - jose/jwk/jwk.c jose/jws/jose.c + jose/jwk/jwk.c + jose/jwk/jose_key.c jose/jws/jws.c jose/jwe/jwe.c jose/jwe/enc/aescbc.c diff --git a/lib/jose/jwk/jose_key.c b/lib/jose/jwk/jose_key.c new file mode 100644 index 000000000..2d754d910 --- /dev/null +++ b/lib/jose/jwk/jose_key.c @@ -0,0 +1,649 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * JOSE-specific JWK code + */ + +#include "private-lib-core.h" +#include "private-lib-jose.h" + +#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) +#include +#endif + +static const char * const kty_names[] = { + "unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */ + "oct", /* LWS_GENCRYPTO_KTY_OCT */ + "RSA", /* LWS_GENCRYPTO_KTY_RSA */ + "EC" /* LWS_GENCRYPTO_KTY_EC */ +}; + +/* + * These are the entire legal token set for names in jwk. + * + * The first version is used to parse a detached single jwk that don't have any + * parent JSON context. The second version is used to parse full jwk objects + * that has a "keys": [ ] array containing the keys. + */ + +const char * const jwk_tok[] = { + "keys[]", /* dummy */ + "e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */ + "kty", /* generic */ + "k", /* symmetric key data */ + "crv", "x", "y", /* EC (also "D") */ + "kid", /* generic */ + "use" /* mutually exclusive with "key_ops" */, + "key_ops" /* mutually exclusive with "use" */, + "x5c", /* generic */ + "alg" /* generic */ +}, * const jwk_outer_tok[] = { + "keys[]", + "keys[].e", "keys[].n", "keys[].d", "keys[].p", "keys[].q", "keys[].dp", + "keys[].dq", "keys[].qi", + + "keys[].kty", "keys[].k", /* generic */ + "keys[].crv", "keys[].x", "keys[].y", /* EC (also "D") */ + "keys[].kid", "keys[].use" /* mutually exclusive with "key_ops" */, + "keys[].key_ops", /* mutually exclusive with "use" */ + "keys[].x5c", "keys[].alg" +}; + +static unsigned short tok_map[] = { + F_RSA | F_EC | F_OCT | F_META | 0xff, + F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E, + F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N, + F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D, + F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P, + F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q, + F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP, + F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ, + F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI, + + F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY, + F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K, + + F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV, + F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X, + F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y, + + F_RSA | F_EC | F_OCT | F_META | JWK_META_KID, + F_RSA | F_EC | F_OCT | F_META | JWK_META_USE, + + F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS, + F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C, + F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG, +}; + +struct lexico { + const char *name; + int idx; + char meta; +} lexico_ec[] = { + { "alg", JWK_META_ALG, 1 }, + { "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 }, + { "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 }, + { "key_ops", JWK_META_KEY_OPS, 1 }, + { "kid", JWK_META_KID, 1 }, + { "kty", JWK_META_KTY, 1 }, + { "use", JWK_META_USE, 1 }, + { "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 }, + { "x5c", JWK_META_X5C, 1 }, + { "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 } +}, lexico_oct[] = { + { "alg", JWK_META_ALG, 1 }, + { "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 }, + { "key_ops", JWK_META_KEY_OPS, 1 }, + { "kid", JWK_META_KID, 1 }, + { "kty", JWK_META_KTY, 1 }, + { "use", JWK_META_USE, 1 }, + { "x5c", JWK_META_X5C, 1 } +}, lexico_rsa[] = { + { "alg", JWK_META_ALG, 1 }, + { "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 }, + { "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 }, + { "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 }, + { "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 }, + { "key_ops", JWK_META_KEY_OPS, 1 }, + { "kid", JWK_META_KID, 1 }, + { "kty", JWK_META_KTY, 1 }, + { "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 }, + { "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 }, + { "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 }, + { "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 }, + { "use", JWK_META_USE, 1 }, + { "x5c", JWK_META_X5C, 1 } +}; + +static int +_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len) +{ + size_t dec_size = (unsigned int)lws_base64_size(len); + int n; + + e->buf = lws_malloc(dec_size, "jwk"); + if (!e->buf) + return -1; + + /* same decoder accepts both url or original styles */ + + n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1); + if (n < 0) + return -1; + e->len = (uint32_t)n; + + return 0; +} + +static int +_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len) +{ + size_t dec_size = (size_t)lws_base64_size(len); + int n; + + e->buf = lws_malloc(dec_size, "jwk"); + if (!e->buf) + return -1; + + /* same decoder accepts both url or original styles */ + + n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1); + if (n < 0) + return -1; + e->len = (uint32_t)n; + + return 0; +} + + +signed char +cb_jwk(struct lejp_ctx *ctx, char reason) +{ + struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user; + struct lws_jwk *jwk = jps->jwk; + unsigned int idx, n; + unsigned short poss; + char dotstar[64]; + + if (reason == LEJPCB_VAL_STR_START) + jps->pos = 0; + + if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1) + /* + * new keys[] member is starting + * + * Until we see some JSON names, it could be anything... + * there is no requirement for kty to be given first and eg, + * ACME specifies the keys must be ordered in lexographic + * order - where kty is not first. + */ + jps->possible = F_RSA | F_EC | F_OCT; + + if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) { + /* we completed parsing a key */ + if (jps->per_key_cb && jps->possible) { + if (jps->per_key_cb(jps->jwk, jps->user)) { + + lwsl_notice("%s: user cb halts import\n", + __func__); + + return -2; + } + + /* clear it down */ + lws_jwk_destroy(jps->jwk); + jps->possible = 0; + } + } + + if (reason == LEJPCB_COMPLETE) { + + /* + * Now we saw the whole jwk and know the key type, let'jwk insist + * that as a whole, it must be consistent and complete. + * + * The tracking of ->possible bits from even before we know the + * kty already makes certain we cannot have key element members + * defined that are inconsistent with the key type. + */ + + for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++) + /* + * All mandataory elements for the key type + * must be present + */ + if ((tok_map[n] & jps->possible) && ( + ((tok_map[n] & (F_M | F_META)) == (F_M | F_META) && + !jwk->meta[tok_map[n] & 0xff].buf) || + ((tok_map[n] & (F_M | F_META)) == F_M && + !jwk->e[tok_map[n] & 0xff].buf))) { + lwsl_notice("%s: missing %s\n", __func__, + jwk_tok[n]); + return -3; + } + + /* + * When the key may be public or public + private, ensure the + * intra-key members related to that are consistent. + * + * Only RSA keys need extra care, since EC keys are already + * confirmed by making CRV, X and Y mandatory and only D + * (the singular private part) optional. For RSA, N and E are + * also already known to be present using mandatory checking. + */ + + /* + * If a private key, it must have all D, P and Q. Public key + * must have none of them. + */ + if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && + !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) && + (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) && + (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) || + (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf && + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) + ) { + lwsl_notice("%s: RSA requires D, P and Q for private\n", + __func__); + return -3; + } + + /* + * If the precomputed private key terms appear, they must all + * appear together. + */ + if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && + !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) && + (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) && + (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) || + (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf && + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf && + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) + ) { + lwsl_notice("%s: RSA DP, DQ, QI must all appear " + "or none\n", __func__); + return -3; + } + + /* + * The precomputed private key terms must not appear without + * the private key itself also appearing. + */ + if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && + !jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) { + lwsl_notice("%s: RSA DP, DQ, QI can appear only with " + "private key\n", __func__); + return -3; + } + + if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA || + jwk->kty == LWS_GENCRYPTO_KTY_EC) && + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) + jwk->private_key = 1; + } + + if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) + return 0; + + if (ctx->path_match == 0 + 1) + return 0; + + idx = tok_map[ctx->path_match - 1]; + if ((idx & 0xff) == 0xff) + return 0; + + switch (idx) { + /* note: kty is not necessarily first... we have to keep track of + * what could match given which element names have already been + * seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk + * not trying to tell us that it'jwk RSA now when we saw a "crv" + * earlier) and then reduce the possibilities to just the one that + * kty told. */ + case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY: + + if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) { + if (!(jps->possible & F_OCT)) + goto elements_mismatch; + jwk->kty = LWS_GENCRYPTO_KTY_OCT; + jps->possible = F_OCT; + goto cont; + } + if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) { + if (!(jps->possible & F_RSA)) + goto elements_mismatch; + jwk->kty = LWS_GENCRYPTO_KTY_RSA; + jps->possible = F_RSA; + goto cont; + } + if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) { + if (!(jps->possible & F_EC)) + goto elements_mismatch; + jwk->kty = LWS_GENCRYPTO_KTY_EC; + jps->possible = F_EC; + goto cont; + } + lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); + lwsl_err("%s: Unknown KTY '%s'\n", __func__, dotstar); + return -1; + + default: +cont: + if (jps->pos + ctx->npos >= (int)sizeof(jps->b64)) + goto bail; + + memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos); + jps->pos += ctx->npos; + + if (reason == LEJPCB_VAL_STR_CHUNK) + return 0; + + /* chunking has been collated */ + + poss = idx & (F_RSA | F_EC | F_OCT); + jps->possible &= poss; + if (!jps->possible) + goto elements_mismatch; + + if (idx & F_META) { + if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f], + jps->b64, (unsigned int)jps->pos) < 0) + goto bail; + + break; + } + + if (idx & F_B64U) { + /* key data... do the base64 decode as needed */ + if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f], + jps->b64, jps->pos) < 0) + goto bail; + + if (jwk->e[idx & 0x7f].len > + LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) { + lwsl_notice("%s: oversize keydata\n", __func__); + goto bail; + } + + return 0; + } + + if (idx & F_B64) { + + /* cert data... do non-urlcoded base64 decode */ + if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f], + jps->b64, jps->pos) < 0) + goto bail; + return 0; + } + + if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f], + jps->b64, (unsigned int)jps->pos) < 0) + goto bail; + break; + } + + return 0; + +elements_mismatch: + lwsl_err("%s: jwk elements mismatch\n", __func__); + +bail: + lwsl_err("%s: element failed\n", __func__); + + return -1; +} + +int +lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user, + const char *in, size_t len) +{ + struct lejp_ctx jctx; + struct lws_jwk_parse_state jps; + int m; + + lws_jwk_init_jps(&jps, jwk, cb, user); + + lejp_construct(&jctx, cb_jwk, &jps, cb ? jwk_outer_tok: jwk_tok, + LWS_ARRAY_SIZE(jwk_tok)); + + m = lejp_parse(&jctx, (uint8_t *)in, (int)len); + lejp_destruct(&jctx); + + if (m < 0) { + lwsl_notice("%s: parse got %d\n", __func__, m); + lws_jwk_destroy(jwk); + return -1; + } + + switch (jwk->kty) { + case LWS_GENCRYPTO_KTY_UNKNOWN: + lwsl_notice("%s: missing or unknown kty\n", __func__); + lws_jwk_destroy(jwk); + return -1; + default: + break; + } + + return 0; +} + + +int +lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len) +{ + char *start = p, *end = &p[*len - 1]; + int n, m, limit, first = 1, asym = 0; + struct lexico *l; + + /* RFC7638 lexicographic order requires + * RSA: e -> kty -> n + * oct: k -> kty + * + * ie, meta and key data elements appear interleaved in name alpha order + */ + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{"); + + switch (jwk->kty) { + case LWS_GENCRYPTO_KTY_OCT: + l = lexico_oct; + limit = LWS_ARRAY_SIZE(lexico_oct); + break; + case LWS_GENCRYPTO_KTY_RSA: + l = lexico_rsa; + limit = LWS_ARRAY_SIZE(lexico_rsa); + asym = 1; + break; + case LWS_GENCRYPTO_KTY_EC: + l = lexico_ec; + limit = LWS_ARRAY_SIZE(lexico_ec); + asym = 1; + break; + default: + return -1; + } + + for (n = 0; n < limit; n++) { + const char *q, *q_end; + char tok[12]; + int pos = 0, f = 1; + + if ((l->meta & 1) && (jwk->meta[l->idx].buf || + l->idx == (int)JWK_META_KTY)) { + + switch (l->idx) { + case JWK_META_KTY: + if (!first) + *p++ = ','; + first = 0; + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"%s\"", + l->name, kty_names[jwk->kty]); + break; + case JWK_META_KEY_OPS: + if (!first) + *p++ = ','; + first = 0; + q = (const char *)jwk->meta[l->idx].buf; + q_end = q + jwk->meta[l->idx].len; + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + "\"%s\":[", l->name); + /* + * For the public version, usages that + * require the private part must be + * snipped + */ + + while (q < q_end) { + if (*q != ' ' && pos < (int)sizeof(tok) - 1) { + tok[pos++] = *q++; + if (q != q_end) + continue; + } + tok[pos] = '\0'; + pos = 0; + if ((flags & LWSJWKF_EXPORT_PRIVATE) || + !asym || (strcmp(tok, "sign") && + strcmp(tok, "encrypt"))) { + if (!f) + *p++ = ','; + f = 0; + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + "\"%s\"", tok); + } + q++; + } + + *p++ = ']'; + + break; + + default: + /* both sig and enc require asym private key */ + if (!(flags & LWSJWKF_EXPORT_PRIVATE) && + asym && l->idx == (int)JWK_META_USE) + break; + if (!first) + *p++ = ','; + first = 0; + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"", + l->name); + lws_strnncpy(p, (const char *)jwk->meta[l->idx].buf, + jwk->meta[l->idx].len, end - p); + p += strlen(p); + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\""); + break; + } + } + + if ((!(l->meta & 1)) && jwk->e[l->idx].buf && + ((flags & LWSJWKF_EXPORT_PRIVATE) || !(l->meta & 2))) { + if (!first) + *p++ = ','; + first = 0; + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"", l->name); + + if (jwk->kty == LWS_GENCRYPTO_KTY_EC && + l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV) { + lws_strnncpy(p, + (const char *)jwk->e[l->idx].buf, + jwk->e[l->idx].len, end - p); + m = (int)strlen(p); + } else + m = lws_jws_base64_enc( + (const char *)jwk->e[l->idx].buf, + jwk->e[l->idx].len, p, lws_ptr_diff_size_t(end, p) - 4); + if (m < 0) { + lwsl_notice("%s: enc failed\n", __func__); + return -1; + } + p += m; + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\""); + } + + l++; + } + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + (flags & LWSJWKF_EXPORT_NOCRLF) ? "}" : "}\n"); + + *len -= lws_ptr_diff(p, start); + + return lws_ptr_diff(p, start); +} + +int +lws_jwk_load(struct lws_jwk *jwk, const char *filename, + lws_jwk_key_import_callback cb, void *user) +{ + unsigned int buflen = 4096; + char *buf = lws_malloc(buflen, "jwk-load"); + int n; + + if (!buf) + return -1; + + n = lws_plat_read_file(filename, buf, buflen); + if (n < 0) + goto bail; + + n = lws_jwk_import(jwk, cb, user, buf, (unsigned int)n); + lws_free(buf); + + return n; +bail: + lws_free(buf); + + return -1; +} + +int +lws_jwk_save(struct lws_jwk *jwk, const char *filename) +{ + int buflen = 4096; + char *buf = lws_malloc((unsigned int)buflen, "jwk-save"); + int n, m; + + if (!buf) + return -1; + + n = lws_jwk_export(jwk, LWSJWKF_EXPORT_PRIVATE, buf, &buflen); + if (n < 0) + goto bail; + + m = lws_plat_write_file(filename, buf, (size_t)n); + + lws_free(buf); + if (m) + return -1; + + return 0; + +bail: + lws_free(buf); + + return -1; +} diff --git a/lib/jose/jwk/jwk.c b/lib/jose/jwk/jwk.c index 592d5a285..d0befd824 100644 --- a/lib/jose/jwk/jwk.c +++ b/lib/jose/jwk/jwk.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 - 2019 Andy Green + * Copyright (C) 2010 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -20,133 +20,17 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. + * + * Shared JWK handling that's the same whether JOSE or COSE */ #include "private-lib-core.h" #include "private-lib-jose.h" -#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT) -#include -#endif - -static const char * const kty_names[] = { - "unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */ - "oct", /* LWS_GENCRYPTO_KTY_OCT */ - "RSA", /* LWS_GENCRYPTO_KTY_RSA */ - "EC" /* LWS_GENCRYPTO_KTY_EC */ -}; - -/* - * These are the entire legal token set for names in jwk. - * - * The first version is used to parse a detached single jwk that don't have any - * parent JSON context. The second version is used to parse full jwk objects - * that has a "keys": [ ] array containing the keys. - */ - -static const char * const jwk_tok[] = { - "keys[]", /* dummy */ - "e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */ - "kty", /* generic */ - "k", /* symmetric key data */ - "crv", "x", "y", /* EC (also "D") */ - "kid", /* generic */ - "use" /* mutually exclusive with "key_ops" */, - "key_ops" /* mutually exclusive with "use" */, - "x5c", /* generic */ - "alg" /* generic */ -}, * const jwk_outer_tok[] = { - "keys[]", - "keys[].e", "keys[].n", "keys[].d", "keys[].p", "keys[].q", "keys[].dp", - "keys[].dq", "keys[].qi", - - "keys[].kty", "keys[].k", /* generic */ - "keys[].crv", "keys[].x", "keys[].y", /* EC (also "D") */ - "keys[].kid", "keys[].use" /* mutually exclusive with "key_ops" */, - "keys[].key_ops", /* mutually exclusive with "use" */ - "keys[].x5c", "keys[].alg" -}; - -/* information about each token declared above */ - -#define F_M (1 << 9) /* Mandatory for key type */ -#define F_B64 (1 << 10) /* Base64 coded octets */ -#define F_B64U (1 << 11) /* Base64 Url coded octets */ -#define F_META (1 << 12) /* JWK key metainformation */ -#define F_RSA (1 << 13) /* RSA key */ -#define F_EC (1 << 14) /* Elliptic curve key */ -#define F_OCT (1 << 15) /* octet key */ - -static unsigned short tok_map[] = { - F_RSA | F_EC | F_OCT | F_META | 0xff, - F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E, - F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N, - F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ, - F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI, - - F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY, - F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K, - - F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV, - F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X, - F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y, - - F_RSA | F_EC | F_OCT | F_META | JWK_META_KID, - F_RSA | F_EC | F_OCT | F_META | JWK_META_USE, - - F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS, - F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C, - F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG, -}; - static const char *meta_names[] = { "kty", "kid", "use", "key_ops", "x5c", "alg" }; -struct lexico { - const char *name; - int idx; - char meta; -} lexico_ec[] = { - { "alg", JWK_META_ALG, 1 }, - { "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 }, - { "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 }, - { "key_ops", JWK_META_KEY_OPS, 1 }, - { "kid", JWK_META_KID, 1 }, - { "kty", JWK_META_KTY, 1 }, - { "use", JWK_META_USE, 1 }, - { "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 }, - { "x5c", JWK_META_X5C, 1 }, - { "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 } -}, lexico_oct[] = { - { "alg", JWK_META_ALG, 1 }, - { "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 }, - { "key_ops", JWK_META_KEY_OPS, 1 }, - { "kid", JWK_META_KID, 1 }, - { "kty", JWK_META_KTY, 1 }, - { "use", JWK_META_USE, 1 }, - { "x5c", JWK_META_X5C, 1 } -}, lexico_rsa[] = { - { "alg", JWK_META_ALG, 1 }, - { "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 }, - { "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 }, - { "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 }, - { "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 }, - { "key_ops", JWK_META_KEY_OPS, 1 }, - { "kid", JWK_META_KID, 1 }, - { "kty", JWK_META_KTY, 1 }, - { "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 }, - { "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 }, - { "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 }, - { "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 }, - { "use", JWK_META_USE, 1 }, - { "x5c", JWK_META_X5C, 1 } -}; - static const char meta_b64[] = { 0, 0, 0, 0, 1, 0 }; static const char *oct_names[] = { @@ -221,7 +105,7 @@ lws_jwk_dump(struct lws_jwk *jwk) return 0; } -static int +int _lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len) { e->buf = lws_malloc(len + 1, "jwk"); @@ -235,46 +119,6 @@ _lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len) return 0; } -static int -_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len) -{ - size_t dec_size = (unsigned int)lws_base64_size(len); - int n; - - e->buf = lws_malloc(dec_size, "jwk"); - if (!e->buf) - return -1; - - /* same decoder accepts both url or original styles */ - - n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1); - if (n < 0) - return -1; - e->len = (uint32_t)n; - - return 0; -} - -static int -_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len) -{ - size_t dec_size = (size_t)lws_base64_size(len); - int n; - - e->buf = lws_malloc(dec_size, "jwk"); - if (!e->buf) - return -1; - - /* same decoder accepts both url or original styles */ - - n = lws_b64_decode_string_len(in, len, (char *)e->buf, (int)dec_size - 1); - if (n < 0) - return -1; - e->len = (uint32_t)n; - - return 0; -} - void lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m) { @@ -296,261 +140,21 @@ lws_jwk_destroy(struct lws_jwk *jwk) lws_jwk_destroy_elements(jwk->meta, LWS_ARRAY_SIZE(jwk->meta)); } -static signed char -cb_jwk(struct lejp_ctx *ctx, char reason) -{ - struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user; - struct lws_jwk *jwk = jps->jwk; - unsigned int idx, n; - unsigned short poss; - char dotstar[64]; - - if (reason == LEJPCB_VAL_STR_START) - jps->pos = 0; - - if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1) - /* - * new keys[] member is starting - * - * Until we see some JSON names, it could be anything... - * there is no requirement for kty to be given first and eg, - * ACME specifies the keys must be ordered in lexographic - * order - where kty is not first. - */ - jps->possible = F_RSA | F_EC | F_OCT; - - if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) { - /* we completed parsing a key */ - if (jps->per_key_cb && jps->possible) { - if (jps->per_key_cb(jps->jwk, jps->user)) { - - lwsl_notice("%s: user cb halts import\n", - __func__); - - return -2; - } - - /* clear it down */ - lws_jwk_destroy(jps->jwk); - jps->possible = 0; - } - } - - if (reason == LEJPCB_COMPLETE) { - - /* - * Now we saw the whole jwk and know the key type, let'jwk insist - * that as a whole, it must be consistent and complete. - * - * The tracking of ->possible bits from even before we know the - * kty already makes certain we cannot have key element members - * defined that are inconsistent with the key type. - */ - - for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++) - /* - * All mandataory elements for the key type - * must be present - */ - if ((tok_map[n] & jps->possible) && ( - ((tok_map[n] & (F_M | F_META)) == (F_M | F_META) && - !jwk->meta[tok_map[n] & 0xff].buf) || - ((tok_map[n] & (F_M | F_META)) == F_M && - !jwk->e[tok_map[n] & 0xff].buf))) { - lwsl_notice("%s: missing %s\n", __func__, - jwk_tok[n]); - return -3; - } - - /* - * When the key may be public or public + private, ensure the - * intra-key members related to that are consistent. - * - * Only RSA keys need extra care, since EC keys are already - * confirmed by making CRV, X and Y mandatory and only D - * (the singular private part) optional. For RSA, N and E are - * also already known to be present using mandatory checking. - */ - - /* - * If a private key, it must have all D, P and Q. Public key - * must have none of them. - */ - if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && - !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) || - (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) - ) { - lwsl_notice("%s: RSA requires D, P and Q for private\n", - __func__); - return -3; - } - - /* - * If the precomputed private key terms appear, they must all - * appear together. - */ - if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && - !(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) && - (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) || - (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) - ) { - lwsl_notice("%s: RSA DP, DQ, QI must all appear " - "or none\n", __func__); - return -3; - } - - /* - * The precomputed private key terms must not appear without - * the private key itself also appearing. - */ - if (jwk->kty == LWS_GENCRYPTO_KTY_RSA && - !jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) { - lwsl_notice("%s: RSA DP, DQ, QI can appear only with " - "private key\n", __func__); - return -3; - } - - if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA || - jwk->kty == LWS_GENCRYPTO_KTY_EC) && - jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) - jwk->private_key = 1; - } - - if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) - return 0; - - if (ctx->path_match == 0 + 1) - return 0; - - idx = tok_map[ctx->path_match - 1]; - if ((idx & 0xff) == 0xff) - return 0; - - switch (idx) { - /* note: kty is not necessarily first... we have to keep track of - * what could match given which element names have already been - * seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk - * not trying to tell us that it'jwk RSA now when we saw a "crv" - * earlier) and then reduce the possibilities to just the one that - * kty told. */ - case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY: - - if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) { - if (!(jps->possible & F_OCT)) - goto elements_mismatch; - jwk->kty = LWS_GENCRYPTO_KTY_OCT; - jps->possible = F_OCT; - goto cont; - } - if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) { - if (!(jps->possible & F_RSA)) - goto elements_mismatch; - jwk->kty = LWS_GENCRYPTO_KTY_RSA; - jps->possible = F_RSA; - goto cont; - } - if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) { - if (!(jps->possible & F_EC)) - goto elements_mismatch; - jwk->kty = LWS_GENCRYPTO_KTY_EC; - jps->possible = F_EC; - goto cont; - } - lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); - lwsl_err("%s: Unknown KTY '%s'\n", __func__, dotstar); - return -1; - - default: -cont: - if (jps->pos + ctx->npos >= (int)sizeof(jps->b64)) - goto bail; - - memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos); - jps->pos += ctx->npos; - - if (reason == LEJPCB_VAL_STR_CHUNK) - return 0; - - /* chunking has been collated */ - - poss = idx & (F_RSA | F_EC | F_OCT); - jps->possible &= poss; - if (!jps->possible) - goto elements_mismatch; - - if (idx & F_META) { - if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f], - jps->b64, (unsigned int)jps->pos) < 0) - goto bail; - - break; - } - - if (idx & F_B64U) { - /* key data... do the base64 decode as needed */ - if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f], - jps->b64, jps->pos) < 0) - goto bail; - - if (jwk->e[idx & 0x7f].len > - LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) { - lwsl_notice("%s: oversize keydata\n", __func__); - goto bail; - } - - return 0; - } - - if (idx & F_B64) { - - /* cert data... do non-urlcoded base64 decode */ - if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f], - jps->b64, jps->pos) < 0) - goto bail; - return 0; - } - - if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f], - jps->b64, (unsigned int)jps->pos) < 0) - goto bail; - break; - } - - return 0; - -elements_mismatch: - lwsl_err("%s: jwk elements mismatch\n", __func__); - -bail: - lwsl_err("%s: element failed\n", __func__); - - return -1; -} - void -lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps, +lws_jwk_init_jps(struct lws_jwk_parse_state *jps, struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user) { if (jwk) memset(jwk, 0, sizeof(*jwk)); - jps->jwk = jwk; - jps->possible = F_RSA | F_EC | F_OCT; - jps->per_key_cb = cb; - jps->user = user; - jps->pos = 0; - - lejp_construct(jctx, cb_jwk, jps, cb ? jwk_outer_tok: jwk_tok, - LWS_ARRAY_SIZE(jwk_tok)); + jps->jwk = jwk; + jps->possible = F_RSA | F_EC | F_OCT; + jps->per_key_cb = cb; + jps->user = user; + jps->pos = 0; + jps->seen = 0; + jps->cose_state = 0; } int @@ -600,6 +204,8 @@ lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk, case LWS_GENCRYPTO_KTY_OCT: sn = (unsigned int)lws_gencrypto_bits_to_bytes(bits); jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(sn, "oct"); + if (!jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf) + return 1; jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = (uint32_t)sn; if (lws_get_random(context, jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, sn) != sn) { @@ -641,183 +247,6 @@ lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk, return 0; } -int -lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user, - const char *in, size_t len) -{ - struct lejp_ctx jctx; - struct lws_jwk_parse_state jps; - int m; - - lws_jwk_init_jps(&jctx, &jps, jwk, cb, user); - - m = lejp_parse(&jctx, (uint8_t *)in, (int)len); - lejp_destruct(&jctx); - - if (m < 0) { - lwsl_notice("%s: parse got %d\n", __func__, m); - lws_jwk_destroy(jwk); - return -1; - } - - switch (jwk->kty) { - case LWS_GENCRYPTO_KTY_UNKNOWN: - lwsl_notice("%s: missing or unknown kyt\n", __func__); - lws_jwk_destroy(jwk); - return -1; - default: - break; - } - - return 0; -} - - -int -lws_jwk_export(struct lws_jwk *jwk, int flags, char *p, int *len) -{ - char *start = p, *end = &p[*len - 1]; - int n, m, limit, first = 1, asym = 0; - struct lexico *l; - - /* RFC7638 lexicographic order requires - * RSA: e -> kty -> n - * oct: k -> kty - * - * ie, meta and key data elements appear interleaved in name alpha order - */ - - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "{"); - - switch (jwk->kty) { - case LWS_GENCRYPTO_KTY_OCT: - l = lexico_oct; - limit = LWS_ARRAY_SIZE(lexico_oct); - break; - case LWS_GENCRYPTO_KTY_RSA: - l = lexico_rsa; - limit = LWS_ARRAY_SIZE(lexico_rsa); - asym = 1; - break; - case LWS_GENCRYPTO_KTY_EC: - l = lexico_ec; - limit = LWS_ARRAY_SIZE(lexico_ec); - asym = 1; - break; - default: - return -1; - } - - for (n = 0; n < limit; n++) { - const char *q, *q_end; - char tok[12]; - int pos = 0, f = 1; - - if ((l->meta & 1) && (jwk->meta[l->idx].buf || - l->idx == (int)JWK_META_KTY)) { - - switch (l->idx) { - case JWK_META_KTY: - if (!first) - *p++ = ','; - first = 0; - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"%s\"", - l->name, kty_names[jwk->kty]); - break; - case JWK_META_KEY_OPS: - if (!first) - *p++ = ','; - first = 0; - q = (const char *)jwk->meta[l->idx].buf; - q_end = q + jwk->meta[l->idx].len; - - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), - "\"%s\":[", l->name); - /* - * For the public version, usages that - * require the private part must be - * snipped - */ - - while (q < q_end) { - if (*q != ' ' && pos < (int)sizeof(tok) - 1) { - tok[pos++] = *q++; - if (q != q_end) - continue; - } - tok[pos] = '\0'; - pos = 0; - if ((flags & LWSJWKF_EXPORT_PRIVATE) || - !asym || (strcmp(tok, "sign") && - strcmp(tok, "encrypt"))) { - if (!f) - *p++ = ','; - f = 0; - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), - "\"%s\"", tok); - } - q++; - } - - *p++ = ']'; - - break; - - default: - /* both sig and enc require asym private key */ - if (!(flags & LWSJWKF_EXPORT_PRIVATE) && - asym && l->idx == (int)JWK_META_USE) - break; - if (!first) - *p++ = ','; - first = 0; - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"", - l->name); - lws_strnncpy(p, (const char *)jwk->meta[l->idx].buf, - jwk->meta[l->idx].len, end - p); - p += strlen(p); - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\""); - break; - } - } - - if ((!(l->meta & 1)) && jwk->e[l->idx].buf && - ((flags & LWSJWKF_EXPORT_PRIVATE) || !(l->meta & 2))) { - if (!first) - *p++ = ','; - first = 0; - - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\"%s\":\"", l->name); - - if (jwk->kty == LWS_GENCRYPTO_KTY_EC && - l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV) { - lws_strnncpy(p, - (const char *)jwk->e[l->idx].buf, - jwk->e[l->idx].len, end - p); - m = (int)strlen(p); - } else - m = lws_jws_base64_enc( - (const char *)jwk->e[l->idx].buf, - jwk->e[l->idx].len, p, lws_ptr_diff_size_t(end, p) - 4); - if (m < 0) { - lwsl_notice("%s: enc failed\n", __func__); - return -1; - } - p += m; - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\""); - } - - l++; - } - - p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), - (flags & LWSJWKF_EXPORT_NOCRLF) ? "}" : "}\n"); - - *len -= lws_ptr_diff(p, start); - - return lws_ptr_diff(p, start); -} - int lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32) { @@ -866,55 +295,3 @@ lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx, return 0; } -int -lws_jwk_load(struct lws_jwk *jwk, const char *filename, - lws_jwk_key_import_callback cb, void *user) -{ - unsigned int buflen = 4096; - char *buf = lws_malloc(buflen, "jwk-load"); - int n; - - if (!buf) - return -1; - - n = lws_plat_read_file(filename, buf, buflen); - if (n < 0) - goto bail; - - n = lws_jwk_import(jwk, cb, user, buf, (unsigned int)n); - lws_free(buf); - - return n; -bail: - lws_free(buf); - - return -1; -} - -int -lws_jwk_save(struct lws_jwk *jwk, const char *filename) -{ - int buflen = 4096; - char *buf = lws_malloc((unsigned int)buflen, "jwk-save"); - int n, m; - - if (!buf) - return -1; - - n = lws_jwk_export(jwk, LWSJWKF_EXPORT_PRIVATE, buf, &buflen); - if (n < 0) - goto bail; - - m = lws_plat_write_file(filename, buf, (size_t)n); - - lws_free(buf); - if (m) - return -1; - - return 0; - -bail: - lws_free(buf); - - return -1; -} diff --git a/lib/jose/jws/jose.c b/lib/jose/jws/jose.c index c9fdf5883..4007865bf 100644 --- a/lib/jose/jws/jose.c +++ b/lib/jose/jws/jose.c @@ -404,7 +404,6 @@ lws_jose_destroy(struct lws_jose *jose) lws_jose_recip_destroy(&jose->recipient[n]); } - static int lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n, char *temp, int *temp_len, int is_jwe) @@ -413,11 +412,14 @@ lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n, struct jose_cb_args args; int m; - if (is_jwe) + if (is_jwe) { /* prepare a context for JOSE epk ephemeral jwk parsing */ - lws_jwk_init_jps(&args.jwk_jctx, &args.jps, + lws_jwk_init_jps(&args.jps, &jose->recipient[jose->recipients].jwk_ephemeral, NULL, NULL); + lejp_construct(&args.jwk_jctx, cb_jwk, &args.jps, + jwk_tok, LWS_ARRAY_SIZE(jwk_tok)); + } args.is_jwe = (unsigned int)is_jwe; args.temp = temp; diff --git a/lib/jose/private-lib-jose.h b/lib/jose/private-lib-jose.h index d5ed26df1..c6508d3b9 100644 --- a/lib/jose/private-lib-jose.h +++ b/lib/jose/private-lib-jose.h @@ -22,14 +22,32 @@ * IN THE SOFTWARE. */ -void -lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m); +/* information about each token declared above */ + +#define F_M (1 << 9) /* Mandatory for key type */ +#define F_B64 (1 << 10) /* Base64 coded octets */ +#define F_B64U (1 << 11) /* Base64 Url coded octets */ +#define F_META (1 << 12) /* JWK key metainformation */ +#define F_RSA (1 << 13) /* RSA key */ +#define F_EC (1 << 14) /* Elliptic curve key */ +#define F_OCT (1 << 15) /* octet key */ void -lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps, - struct lws_jwk *jwk, lws_jwk_key_import_callback cb, - void *user); +lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m); int lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk, char *out, size_t out_len); + +int +_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len); + +void +lws_jwk_init_jps(struct lws_jwk_parse_state *jps, + struct lws_jwk *jwk, lws_jwk_key_import_callback cb, + void *user); + +signed char +cb_jwk(struct lejp_ctx *ctx, char reason); + +extern const char * const jwk_tok[19], * const jwk_outer_tok[19]; diff --git a/lib/tls/mbedtls/lws-genec.c b/lib/tls/mbedtls/lws-genec.c index db7f67f67..bae22e9f0 100644 --- a/lib/tls/mbedtls/lws-genec.c +++ b/lib/tls/mbedtls/lws-genec.c @@ -53,7 +53,7 @@ const struct lws_ec_curves lws_ec_curves[] = { static int lws_genec_keypair_import(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side, - struct lws_gencrypto_keyelem *el) + const struct lws_gencrypto_keyelem *el) { const struct lws_ec_curves *curve; mbedtls_ecp_keypair kp; @@ -202,7 +202,7 @@ lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el, int lws_genecdsa_set_key(struct lws_genec_ctx *ctx, - struct lws_gencrypto_keyelem *el) + const struct lws_gencrypto_keyelem *el) { if (ctx->genec_alg != LEGENEC_ECDSA) return -1; diff --git a/lib/tls/mbedtls/lws-genrsa.c b/lib/tls/mbedtls/lws-genrsa.c index 35883d2a3..292b1ac1a 100644 --- a/lib/tls/mbedtls/lws-genrsa.c +++ b/lib/tls/mbedtls/lws-genrsa.c @@ -41,7 +41,8 @@ lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el) static int mode_map[] = { MBEDTLS_RSA_PKCS_V15, MBEDTLS_RSA_PKCS_V21 }; int -lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el, +lws_genrsa_create(struct lws_genrsa_ctx *ctx, + const struct lws_gencrypto_keyelem *el, struct lws_context *context, enum enum_genrsa_mode mode, enum lws_genhash_types oaep_hashid) { @@ -169,7 +170,7 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx, }; for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) - if (mbedtls_mpi_size(mpi[n])) { + if (mpi[n] && mbedtls_mpi_size(mpi[n])) { el[n].buf = lws_malloc( mbedtls_mpi_size(mpi[n]), "genrsakey"); if (!el[n].buf) diff --git a/lib/tls/mbedtls/mbedtls-x509.c b/lib/tls/mbedtls/mbedtls-x509.c index 8d372c381..e20e07fc1 100644 --- a/lib/tls/mbedtls/mbedtls-x509.c +++ b/lib/tls/mbedtls/mbedtls-x509.c @@ -402,7 +402,7 @@ lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509, mpi[LWS_GENCRYPTO_RSA_KEYEL_DQ] = &rsactx->MBEDTLS_PRIVATE(DQ); mpi[LWS_GENCRYPTO_RSA_KEYEL_QI] = &rsactx->MBEDTLS_PRIVATE(QP); - count = LWS_GENCRYPTO_RSA_KEYEL_COUNT; + count = LWS_GENCRYPTO_RSA_KEYEL_QI + 1; n = LWS_GENCRYPTO_RSA_KEYEL_E; break; diff --git a/lib/tls/openssl/lws-genec.c b/lib/tls/openssl/lws-genec.c index 5fd644d01..f7482495d 100644 --- a/lib/tls/openssl/lws-genec.c +++ b/lib/tls/openssl/lws-genec.c @@ -102,7 +102,8 @@ const struct lws_ec_curves lws_ec_curves[4] = { }; static int -lws_genec_eckey_import(int nid, EVP_PKEY *pkey, struct lws_gencrypto_keyelem *el) +lws_genec_eckey_import(int nid, EVP_PKEY *pkey, + const struct lws_gencrypto_keyelem *el) { EC_KEY *ec = EC_KEY_new_by_curve_name(nid); BIGNUM *bn_d, *bn_x, *bn_y; @@ -184,7 +185,8 @@ bail: static int lws_genec_keypair_import(struct lws_genec_ctx *ctx, const struct lws_ec_curves *curve_table, - EVP_PKEY_CTX **pctx, struct lws_gencrypto_keyelem *el) + EVP_PKEY_CTX **pctx, + const struct lws_gencrypto_keyelem *el) { EVP_PKEY *pkey = NULL; const struct lws_ec_curves *curve; @@ -273,7 +275,7 @@ lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el, int lws_genecdsa_set_key(struct lws_genec_ctx *ctx, - struct lws_gencrypto_keyelem *el) + const struct lws_gencrypto_keyelem *el) { if (ctx->genec_alg != LEGENEC_ECDSA) return -1; @@ -486,6 +488,7 @@ lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in, uint8_t *sig, size_t sig_len) { int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits); + size_t hs = lws_genhash_size(hash_type); const BIGNUM *r = NULL, *s = NULL; ECDSA_SIG *ecdsasig; EC_KEY *eckey; @@ -498,9 +501,9 @@ lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in, if (!ctx->has_private) return -1; - if ((int)sig_len < keybytes * 2) { + if ((int)sig_len != (int)(keybytes * 2)) { lwsl_notice("%s: sig buff %d < %d\n", __func__, - (int)sig_len, keybytes * 2); + (int)sig_len, (int)(hs * 2)); return -1; } @@ -525,7 +528,7 @@ lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in, * 4. The resulting 64-octet sequence is the JWS Signature value. */ - ecdsasig = ECDSA_do_sign(in, (int)lws_genhash_size(hash_type), eckey); + ecdsasig = ECDSA_do_sign(in, (int)hs, eckey); EC_KEY_free(eckey); if (!ecdsasig) { lwsl_notice("%s: ECDSA_do_sign fail\n", __func__); @@ -567,8 +570,8 @@ lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in, enum lws_genhash_types hash_type, int keybits, const uint8_t *sig, size_t sig_len) { - int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits), - hlen = (int)lws_genhash_size(hash_type); + int ret = -1, n, hlen = (int)lws_genhash_size(hash_type), + keybytes = lws_gencrypto_bits_to_bytes(keybits); ECDSA_SIG *ecsig = ECDSA_SIG_new(); BIGNUM *r = NULL, *s = NULL; EC_KEY *eckey; @@ -580,7 +583,7 @@ lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in, goto bail; if ((int)sig_len != keybytes * 2) { - lwsl_err("%s: sig buf too small %d vs %d\n", __func__, + lwsl_err("%s: sig buf size %d vs %d\n", __func__, (int)sig_len, keybytes * 2); goto bail; } @@ -620,7 +623,7 @@ lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in, n = ECDSA_do_verify(in, hlen, ecsig, eckey); EC_KEY_free(eckey); if (n != 1) { - lwsl_err("%s: ECDSA_do_verify fail\n", __func__); + lwsl_err("%s: ECDSA_do_verify fail, hlen %d\n", __func__, (int)hlen); lws_tls_err_describe_clear(); goto bail; } diff --git a/lib/tls/openssl/lws-genhash.c b/lib/tls/openssl/lws-genhash.c index ba140e4c4..11f2fdedf 100644 --- a/lib/tls/openssl/lws-genhash.c +++ b/lib/tls/openssl/lws-genhash.c @@ -83,12 +83,16 @@ lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result) unsigned int len; int ret = 0; + if (!ctx->mdctx) + return 0; + if (result) ret = EVP_DigestFinal_ex(ctx->mdctx, result, &len) != 1; (void)len; EVP_MD_CTX_destroy(ctx->mdctx); + ctx->mdctx = NULL; return ret; } diff --git a/lib/tls/openssl/lws-genrsa.c b/lib/tls/openssl/lws-genrsa.c index ad8dbd544..4cc23f029 100644 --- a/lib/tls/openssl/lws-genrsa.c +++ b/lib/tls/openssl/lws-genrsa.c @@ -77,7 +77,8 @@ bail: } int -lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el, +lws_genrsa_create(struct lws_genrsa_ctx *ctx, + const struct lws_gencrypto_keyelem *el, struct lws_context *context, enum enum_genrsa_mode mode, enum lws_genhash_types oaep_hashid) { @@ -293,7 +294,8 @@ lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in, switch(ctx->mode) { case LGRSAM_PKCS1_1_5: - n = RSA_verify(n, in, (unsigned int)h, (uint8_t *)sig, (unsigned int)sig_len, ctx->rsa); + n = RSA_verify(n, in, (unsigned int)h, (uint8_t *)sig, + (unsigned int)sig_len, ctx->rsa); break; case LGRSAM_PKCS1_OAEP_PSS: md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type); diff --git a/minimal-examples/api-tests/api-test-cose/CMakeLists.txt b/minimal-examples/api-tests/api-test-cose/CMakeLists.txt new file mode 100644 index 000000000..bd6b14284 --- /dev/null +++ b/minimal-examples/api-tests/api-test-cose/CMakeLists.txt @@ -0,0 +1,29 @@ +project(lws-api-test-cose C) +cmake_minimum_required(VERSION 2.8.12) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-api-test-cose) +set(SRCS main.c keys.c sign.c) + +set(requirements 1) +require_lws_config(LWS_WITH_COSE 1 requirements) +require_lws_config(LWS_WITH_CBOR 1 requirements) + +if (requirements) + + add_executable(${SAMP} ${SRCS}) + + if (NOT (LWS_WITH_MBEDTLS AND NOT LWS_HAVE_mbedtls_internal_aes_encrypt)) + add_test(NAME api-test-cose COMMAND lws-api-test-cose) + endif() + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() +endif() diff --git a/minimal-examples/api-tests/api-test-cose/README.md b/minimal-examples/api-tests/api-test-cose/README.md new file mode 100644 index 000000000..74034c793 --- /dev/null +++ b/minimal-examples/api-tests/api-test-cose/README.md @@ -0,0 +1,22 @@ +# lws api test lwsac + +Demonstrates how to use and performs selftests for lwsac + +## build + +``` + $ cmake . && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 + +``` + $ ./lws-api-test-lwsac +[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac +[2018/10/09 09:14:17:4835] USER: Completed: PASS +``` + diff --git a/minimal-examples/api-tests/api-test-cose/keys.c b/minimal-examples/api-tests/api-test-cose/keys.c new file mode 100644 index 000000000..134784d11 --- /dev/null +++ b/minimal-examples/api-tests/api-test-cose/keys.c @@ -0,0 +1,931 @@ +/* + * lws-api-test-jose - RFC8152 cose_key tests + * + * Written in 2010-2021 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * Raw key CBOR created from descriptions at + * + * https://github.com/cose-wg/Examples/blob/master/KeySet.txt + */ + +#include +#include +#include + +static int +key_import_cb(struct lws_cose_key *s, void *user) +{ + lwsl_notice("%s: key type %lld\n", __func__, (long long)s->kty); + + return 0; +} + +static const uint8_t + cose_key1[] = { + 0xa6, 0x01, 0x02, 0x02, 0x62, + 0x31, 0x31, 0x20, 0x01, 0x21, + 0x58, 0x20, 0xba, 0xc5, 0xb1, + 0x1c, 0xad, 0x8f, 0x99, 0xf9, + 0xc7, 0x2b, 0x05, 0xcf, 0x4b, + 0x9e, 0x26, 0xd2, 0x44, 0xdc, + 0x18, 0x9f, 0x74, 0x52, 0x28, + 0x25, 0x5a, 0x21, 0x9a, 0x86, + 0xd6, 0xa0, 0x9e, 0xff, 0x22, + 0x58, 0x20, 0x20, 0x13, 0x8b, + 0xf8, 0x2d, 0xc1, 0xb6, 0xd5, + 0x62, 0xbe, 0x0f, 0xa5, 0x4a, + 0xb7, 0x80, 0x4a, 0x3a, 0x64, + 0xb6, 0xd7, 0x2c, 0xcf, 0xed, + 0x6b, 0x6f, 0xb6, 0xed, 0x28, + 0xbb, 0xfc, 0x11, 0x7e, 0x23, + 0x58, 0x20, 0x57, 0xc9, 0x20, + 0x77, 0x66, 0x41, 0x46, 0xe8, + 0x76, 0x76, 0x0c, 0x95, 0x20, + 0xd0, 0x54, 0xaa, 0x93, 0xc3, + 0xaf, 0xb0, 0x4e, 0x30, 0x67, + 0x05, 0xdb, 0x60, 0x90, 0x30, + 0x85, 0x07, 0xb4, 0xd3 }, + cose_key2[] = { + 0xa6, 0x01, 0x02, 0x02, 0x78, + 0x24, 0x6d, 0x65, 0x72, 0x69, + 0x61, 0x64, 0x6f, 0x63, 0x2e, + 0x62, 0x72, 0x61, 0x6e, 0x64, + 0x79, 0x62, 0x75, 0x63, 0x6b, + 0x40, 0x62, 0x75, 0x63, 0x6b, + 0x6c, 0x61, 0x6e, 0x64, 0x2e, + 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x20, 0x01, 0x21, + 0x58, 0x20, 0x65, 0xed, 0xa5, + 0xa1, 0x25, 0x77, 0xc2, 0xba, + 0xe8, 0x29, 0x43, 0x7f, 0xe3, + 0x38, 0x70, 0x1a, 0x10, 0xaa, + 0xa3, 0x75, 0xe1, 0xbb, 0x5b, + 0x5d, 0xe1, 0x08, 0xde, 0x43, + 0x9c, 0x08, 0x55, 0x1d, 0x22, + 0x58, 0x20, 0x1e, 0x52, 0xed, + 0x75, 0x70, 0x11, 0x63, 0xf7, + 0xf9, 0xe4, 0x0d, 0xdf, 0x9f, + 0x34, 0x1b, 0x3d, 0xc9, 0xba, + 0x86, 0x0a, 0xf7, 0xe0, 0xca, + 0x7c, 0xa7, 0xe9, 0xee, 0xcd, + 0x00, 0x84, 0xd1, 0x9c, 0x23, + 0x58, 0x20, 0xaf, 0xf9, 0x07, + 0xc9, 0x9f, 0x9a, 0xd3, 0xaa, + 0xe6, 0xc4, 0xcd, 0xf2, 0x11, + 0x22, 0xbc, 0xe2, 0xbd, 0x68, + 0xb5, 0x28, 0x3e, 0x69, 0x07, + 0x15, 0x4a, 0xd9, 0x11, 0x84, + 0x0f, 0xa2, 0x08, 0xcf }, + + cose_key3[] = { 0xa3, 0x01, 0x04, 0x02, 0x6a, + 0x6f, 0x75, 0x72, 0x2d, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, + 0x20, 0x58, 0x20, 0x84, 0x9b, + 0x57, 0x21, 0x9d, 0xae, 0x48, + 0xde, 0x64, 0x6d, 0x07, 0xdb, + 0xb5, 0x33, 0x56, 0x6e, 0x97, + 0x66, 0x86, 0x45, 0x7c, 0x14, + 0x91, 0xbe, 0x3a, 0x76, 0xdc, + 0xea, 0x6c, 0x42, 0x71, 0x88 }, + + cose_key4[] = { 0xa6, 0x01, 0x02, 0x02, 0x78, + 0x1e, 0x62, 0x69, 0x6c, 0x62, + 0x6f, 0x2e, 0x62, 0x61, 0x67, + 0x67, 0x69, 0x6e, 0x73, 0x40, + 0x68, 0x6f, 0x62, 0x62, 0x69, + 0x74, 0x6f, 0x6e, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x20, 0x03, 0x21, 0x58, + 0x42, 0x00, 0x72, 0x99, 0x2c, + 0xb3, 0xac, 0x08, 0xec, 0xf3, + 0xe5, 0xc6, 0x3d, 0xed, 0xec, + 0x0d, 0x51, 0xa8, 0xc1, 0xf7, + 0x9e, 0xf2, 0xf8, 0x2f, 0x94, + 0xf3, 0xc7, 0x37, 0xbf, 0x5d, + 0xe7, 0x98, 0x66, 0x71, 0xea, + 0xc6, 0x25, 0xfe, 0x82, 0x57, + 0xbb, 0xd0, 0x39, 0x46, 0x44, + 0xca, 0xaa, 0x3a, 0xaf, 0x8f, + 0x27, 0xa4, 0x58, 0x5f, 0xbb, + 0xca, 0xd0, 0xf2, 0x45, 0x76, + 0x20, 0x08, 0x5e, 0x5c, 0x8f, + 0x42, 0xad, 0x22, 0x58, 0x42, + 0x01, 0xdc, 0xa6, 0x94, 0x7b, + 0xce, 0x88, 0xbc, 0x57, 0x90, + 0x48, 0x5a, 0xc9, 0x74, 0x27, + 0x34, 0x2b, 0xc3, 0x5f, 0x88, + 0x7d, 0x86, 0xd6, 0x5a, 0x08, + 0x93, 0x77, 0xe2, 0x47, 0xe6, + 0x0b, 0xaa, 0x55, 0xe4, 0xe8, + 0x50, 0x1e, 0x2a, 0xda, 0x57, + 0x24, 0xac, 0x51, 0xd6, 0x90, + 0x90, 0x08, 0x03, 0x3e, 0xbc, + 0x10, 0xac, 0x99, 0x9b, 0x9d, + 0x7f, 0x5c, 0xc2, 0x51, 0x9f, + 0x3f, 0xe1, 0xea, 0x1d, 0x94, + 0x75, 0x23, 0x58, 0x42, 0x00, + 0x08, 0x51, 0x38, 0xdd, 0xab, + 0xf5, 0xca, 0x97, 0x5f, 0x58, + 0x60, 0xf9, 0x1a, 0x08, 0xe9, + 0x1d, 0x6d, 0x5f, 0x9a, 0x76, + 0xad, 0x40, 0x18, 0x76, 0x6a, + 0x47, 0x66, 0x80, 0xb5, 0x5c, + 0xd3, 0x39, 0xe8, 0xab, 0x6c, + 0x72, 0xb5, 0xfa, 0xcd, 0xb2, + 0xa2, 0xa5, 0x0a, 0xc2, 0x5b, + 0xd0, 0x86, 0x64, 0x7d, 0xd3, + 0xe2, 0xe6, 0xe9, 0x9e, 0x84, + 0xca, 0x2c, 0x36, 0x09, 0xfd, + 0xf1, 0x77, 0xfe, 0xb2, 0x6d }, + cose_key5[] = { 0xa3, 0x01, 0x04, 0x02, 0x6b, + 0x6f, 0x75, 0x72, 0x2d, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, + 0x32, 0x20, 0x50, 0x84, 0x9b, + 0x57, 0x86, 0x45, 0x7c, 0x14, + 0x91, 0xbe, 0x3a, 0x76, 0xdc, + 0xea, 0x6c, 0x42, 0x71 }, + + cose_key6[] = { 0xa6, 0x01, 0x02, 0x02, 0x78, + 0x21, 0x70, 0x65, 0x72, 0x65, + 0x67, 0x72, 0x69, 0x6e, 0x2e, + 0x74, 0x6f, 0x6f, 0x6b, 0x40, + 0x74, 0x75, 0x63, 0x6b, 0x62, + 0x6f, 0x72, 0x6f, 0x75, 0x67, + 0x68, 0x2e, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x20, + 0x01, 0x21, 0x58, 0x20, 0x98, + 0xf5, 0x0a, 0x4f, 0xf6, 0xc0, + 0x58, 0x61, 0xc8, 0x86, 0x0d, + 0x13, 0xa6, 0x38, 0xea, 0x56, + 0xc3, 0xf5, 0xad, 0x75, 0x90, + 0xbb, 0xfb, 0xf0, 0x54, 0xe1, + 0xc7, 0xb4, 0xd9, 0x1d, 0x62, + 0x80, 0x22, 0x58, 0x20, 0xf0, + 0x14, 0x00, 0xb0, 0x89, 0x86, + 0x78, 0x04, 0xb8, 0xe9, 0xfc, + 0x96, 0xc3, 0x93, 0x21, 0x61, + 0xf1, 0x93, 0x4f, 0x42, 0x23, + 0x06, 0x91, 0x70, 0xd9, 0x24, + 0xb7, 0xe0, 0x3b, 0xf8, 0x22, + 0xbb, 0x23, 0x58, 0x20, 0x02, + 0xd1, 0xf7, 0xe6, 0xf2, 0x6c, + 0x43, 0xd4, 0x86, 0x8d, 0x87, + 0xce, 0xb2, 0x35, 0x31, 0x61, + 0x74, 0x0a, 0xac, 0xf1, 0xf7, + 0x16, 0x36, 0x47, 0x98, 0x4b, + 0x52, 0x2a, 0x84, 0x8d, 0xf1, + 0xc3 }, + cose_key7[] = { 0xa3, 0x01, 0x04, 0x02, 0x58, + 0x24, 0x30, 0x31, 0x38, 0x63, + 0x30, 0x61, 0x65, 0x35, 0x2d, + 0x34, 0x64, 0x39, 0x62, 0x2d, + 0x34, 0x37, 0x31, 0x62, 0x2d, + 0x62, 0x66, 0x64, 0x36, 0x2d, + 0x65, 0x65, 0x66, 0x33, 0x31, + 0x34, 0x62, 0x63, 0x37, 0x30, + 0x33, 0x37, 0x20, 0x58, 0x20, + 0x84, 0x9b, 0x57, 0x21, 0x9d, + 0xae, 0x48, 0xde, 0x64, 0x6d, + 0x07, 0xdb, 0xb5, 0x33, 0x56, + 0x6e, 0x97, 0x66, 0x86, 0x45, + 0x7c, 0x14, 0x91, 0xbe, 0x3a, + 0x76, 0xdc, 0xea, 0x6c, 0x42, + 0x71, 0x88 }, + + cose_key8[] = { + /* kid "sec-48" for hmac 384 */ + + 0xa3, 0x01, 0x04, 0x02, 0x66, + 0x73, 0x65, 0x63, 0x2d, 0x34, + 0x38, 0x20, 0x58, 0x30, 0x84, + 0x9b, 0x57, 0x21, 0x9d, 0xae, + 0x48, 0xde, 0x64, 0x6d, 0x07, + 0xdb, 0xb5, 0x33, 0x56, 0x6e, + 0x97, 0x66, 0x86, 0x45, 0x7c, + 0x14, 0x91, 0xbe, 0x3a, 0x76, + 0xdc, 0xea, 0x6c, 0x42, 0x71, + 0x88, 0x00, 0x11, 0x22, 0x33, + 0x77, 0x88, 0x99, 0xaa, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28 + }, + + cose_key9[] = { + /* kid "sec-64" for hmac 512 */ + + 0xa3, 0x01, 0x04, 0x02, 0x46, + 0x73, 0x65, 0x63, 0x2d, 0x36, + 0x34, 0x20, 0x58, 0x40, 0x84, + 0x9b, 0x57, 0x21, 0x9d, 0xae, + 0x48, 0xde, 0x64, 0x6d, 0x07, + 0xdb, 0xb5, 0x33, 0x56, 0x6e, + 0x97, 0x66, 0x86, 0x45, 0x7c, + 0x14, 0x91, 0xbe, 0x3a, 0x76, + 0xdc, 0xea, 0x6c, 0x42, 0x71, + 0x88, 0x00, 0x11, 0x22, 0x33, + 0x77, 0x88, 0x99, 0xaa, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0xaa, 0xbb, 0xcc, + 0xdd, 0xee, 0xff, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xa0, 0xb1, + 0xb2, 0xb3, 0xb4, + }, + + cose_key10[] = { /* kid "11" (again) ed22519 OKP key */ + 0xa5, 0x01, 0x01, 0x02, 0x42, + 0x31, 0x31, 0x20, 0x06, 0x21, + 0x58, 0x20, 0xd7, 0x5a, 0x98, + 0x01, 0x82, 0xb1, 0x0a, 0xb7, + 0xd5, 0x4b, 0xfe, 0xd3, 0xc9, + 0x64, 0x07, 0x3a, 0x0e, 0xe1, + 0x72, 0xf3, 0xda, 0xa6, 0x23, + 0x25, 0xaf, 0x02, 0x1a, 0x68, + 0xf7, 0x07, 0x51, 0x1a, 0x23, + 0x58, 0x20, 0x9d, 0x61, 0xb1, + 0x9d, 0xef, 0xfd, 0x5a, 0x60, + 0xba, 0x84, 0x4a, 0xf4, 0x92, + 0xec, 0x2c, 0xc4, 0x44, 0x49, + 0xc5, 0x69, 0x7b, 0x32, 0x69, + 0x19, 0x70, 0x3b, 0xac, 0x03, + 0x1c, 0xae, 0x7f, 0x60 + }, + + cose_key_set1[] = { + + 0x89, + + 0xa6, 0x01, 0x02, 0x02, 0x42, + 0x31, 0x31, 0x20, 0x01, 0x21, + 0x58, 0x20, 0xba, 0xc5, 0xb1, + 0x1c, 0xad, 0x8f, 0x99, 0xf9, + 0xc7, 0x2b, 0x05, 0xcf, 0x4b, + 0x9e, 0x26, 0xd2, 0x44, 0xdc, + 0x18, 0x9f, 0x74, 0x52, 0x28, + 0x25, 0x5a, 0x21, 0x9a, 0x86, + 0xd6, 0xa0, 0x9e, 0xff, 0x22, + 0x58, 0x20, 0x20, 0x13, 0x8b, + 0xf8, 0x2d, 0xc1, 0xb6, 0xd5, + 0x62, 0xbe, 0x0f, 0xa5, 0x4a, + 0xb7, 0x80, 0x4a, 0x3a, 0x64, + 0xb6, 0xd7, 0x2c, 0xcf, 0xed, + 0x6b, 0x6f, 0xb6, 0xed, 0x28, + 0xbb, 0xfc, 0x11, 0x7e, 0x23, + 0x58, 0x20, 0x57, 0xc9, 0x20, + 0x77, 0x66, 0x41, 0x46, 0xe8, + 0x76, 0x76, 0x0c, 0x95, 0x20, + 0xd0, 0x54, 0xaa, 0x93, 0xc3, + 0xaf, 0xb0, 0x4e, 0x30, 0x67, + 0x05, 0xdb, 0x60, 0x90, 0x30, + 0x85, 0x07, 0xb4, 0xd3, + + 0xa6, 0x01, 0x02, 0x02, 0x58, + 0x24, 0x6d, 0x65, 0x72, 0x69, + 0x61, 0x64, 0x6f, 0x63, 0x2e, + 0x62, 0x72, 0x61, 0x6e, 0x64, + 0x79, 0x62, 0x75, 0x63, 0x6b, + 0x40, 0x62, 0x75, 0x63, 0x6b, + 0x6c, 0x61, 0x6e, 0x64, 0x2e, + 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x20, 0x01, 0x21, + 0x58, 0x20, 0x65, 0xed, 0xa5, + 0xa1, 0x25, 0x77, 0xc2, 0xba, + 0xe8, 0x29, 0x43, 0x7f, 0xe3, + 0x38, 0x70, 0x1a, 0x10, 0xaa, + 0xa3, 0x75, 0xe1, 0xbb, 0x5b, + 0x5d, 0xe1, 0x08, 0xde, 0x43, + 0x9c, 0x08, 0x55, 0x1d, 0x22, + 0x58, 0x20, 0x1e, 0x52, 0xed, + 0x75, 0x70, 0x11, 0x63, 0xf7, + 0xf9, 0xe4, 0x0d, 0xdf, 0x9f, + 0x34, 0x1b, 0x3d, 0xc9, 0xba, + 0x86, 0x0a, 0xf7, 0xe0, 0xca, + 0x7c, 0xa7, 0xe9, 0xee, 0xcd, + 0x00, 0x84, 0xd1, 0x9c, 0x23, + 0x58, 0x20, 0xaf, 0xf9, 0x07, + 0xc9, 0x9f, 0x9a, 0xd3, 0xaa, + 0xe6, 0xc4, 0xcd, 0xf2, 0x11, + 0x22, 0xbc, 0xe2, 0xbd, 0x68, + 0xb5, 0x28, 0x3e, 0x69, 0x07, + 0x15, 0x4a, 0xd9, 0x11, 0x84, + 0x0f, 0xa2, 0x08, 0xcf, + + 0xa3, 0x01, 0x04, 0x02, 0x4a, + 0x6f, 0x75, 0x72, 0x2d, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, + 0x20, 0x58, 0x20, 0x84, 0x9b, + 0x57, 0x21, 0x9d, 0xae, 0x48, + 0xde, 0x64, 0x6d, 0x07, 0xdb, + 0xb5, 0x33, 0x56, 0x6e, 0x97, + 0x66, 0x86, 0x45, 0x7c, 0x14, + 0x91, 0xbe, 0x3a, 0x76, 0xdc, + 0xea, 0x6c, 0x42, 0x71, 0x88, + + 0xa6, 0x01, 0x02, 0x02, 0x58, + 0x1e, 0x62, 0x69, 0x6c, 0x62, + 0x6f, 0x2e, 0x62, 0x61, 0x67, + 0x67, 0x69, 0x6e, 0x73, 0x40, + 0x68, 0x6f, 0x62, 0x62, 0x69, + 0x74, 0x6f, 0x6e, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x20, 0x03, 0x21, 0x58, + 0x42, 0x00, 0x72, 0x99, 0x2c, + 0xb3, 0xac, 0x08, 0xec, 0xf3, + 0xe5, 0xc6, 0x3d, 0xed, 0xec, + 0x0d, 0x51, 0xa8, 0xc1, 0xf7, + 0x9e, 0xf2, 0xf8, 0x2f, 0x94, + 0xf3, 0xc7, 0x37, 0xbf, 0x5d, + 0xe7, 0x98, 0x66, 0x71, 0xea, + 0xc6, 0x25, 0xfe, 0x82, 0x57, + 0xbb, 0xd0, 0x39, 0x46, 0x44, + 0xca, 0xaa, 0x3a, 0xaf, 0x8f, + 0x27, 0xa4, 0x58, 0x5f, 0xbb, + 0xca, 0xd0, 0xf2, 0x45, 0x76, + 0x20, 0x08, 0x5e, 0x5c, 0x8f, + 0x42, 0xad, 0x22, 0x58, 0x42, + 0x01, 0xdc, 0xa6, 0x94, 0x7b, + 0xce, 0x88, 0xbc, 0x57, 0x90, + 0x48, 0x5a, 0xc9, 0x74, 0x27, + 0x34, 0x2b, 0xc3, 0x5f, 0x88, + 0x7d, 0x86, 0xd6, 0x5a, 0x08, + 0x93, 0x77, 0xe2, 0x47, 0xe6, + 0x0b, 0xaa, 0x55, 0xe4, 0xe8, + 0x50, 0x1e, 0x2a, 0xda, 0x57, + 0x24, 0xac, 0x51, 0xd6, 0x90, + 0x90, 0x08, 0x03, 0x3e, 0xbc, + 0x10, 0xac, 0x99, 0x9b, 0x9d, + 0x7f, 0x5c, 0xc2, 0x51, 0x9f, + 0x3f, 0xe1, 0xea, 0x1d, 0x94, + 0x75, 0x23, 0x58, 0x42, 0x00, + 0x08, 0x51, 0x38, 0xdd, 0xab, + 0xf5, 0xca, 0x97, 0x5f, 0x58, + 0x60, 0xf9, 0x1a, 0x08, 0xe9, + 0x1d, 0x6d, 0x5f, 0x9a, 0x76, + 0xad, 0x40, 0x18, 0x76, 0x6a, + 0x47, 0x66, 0x80, 0xb5, 0x5c, + 0xd3, 0x39, 0xe8, 0xab, 0x6c, + 0x72, 0xb5, 0xfa, 0xcd, 0xb2, + 0xa2, 0xa5, 0x0a, 0xc2, 0x5b, + 0xd0, 0x86, 0x64, 0x7d, 0xd3, + 0xe2, 0xe6, 0xe9, 0x9e, 0x84, + 0xca, 0x2c, 0x36, 0x09, 0xfd, + 0xf1, 0x77, 0xfe, 0xb2, 0x6d, + + 0xa3, 0x01, 0x04, 0x02, 0x4b, + 0x6f, 0x75, 0x72, 0x2d, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, + 0x32, 0x20, 0x50, 0x84, 0x9b, + 0x57, 0x86, 0x45, 0x7c, 0x14, + 0x91, 0xbe, 0x3a, 0x76, 0xdc, + 0xea, 0x6c, 0x42, 0x71, + + 0xa6, 0x01, 0x02, 0x02, 0x58, + 0x21, 0x70, 0x65, 0x72, 0x65, + 0x67, 0x72, 0x69, 0x6e, 0x2e, + 0x74, 0x6f, 0x6f, 0x6b, 0x40, + 0x74, 0x75, 0x63, 0x6b, 0x62, + 0x6f, 0x72, 0x6f, 0x75, 0x67, + 0x68, 0x2e, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x20, + 0x01, 0x21, 0x58, 0x20, 0x98, + 0xf5, 0x0a, 0x4f, 0xf6, 0xc0, + 0x58, 0x61, 0xc8, 0x86, 0x0d, + 0x13, 0xa6, 0x38, 0xea, 0x56, + 0xc3, 0xf5, 0xad, 0x75, 0x90, + 0xbb, 0xfb, 0xf0, 0x54, 0xe1, + 0xc7, 0xb4, 0xd9, 0x1d, 0x62, + 0x80, 0x22, 0x58, 0x20, 0xf0, + 0x14, 0x00, 0xb0, 0x89, 0x86, + 0x78, 0x04, 0xb8, 0xe9, 0xfc, + 0x96, 0xc3, 0x93, 0x21, 0x61, + 0xf1, 0x93, 0x4f, 0x42, 0x23, + 0x06, 0x91, 0x70, 0xd9, 0x24, + 0xb7, 0xe0, 0x3b, 0xf8, 0x22, + 0xbb, 0x23, 0x58, 0x20, 0x02, + 0xd1, 0xf7, 0xe6, 0xf2, 0x6c, + 0x43, 0xd4, 0x86, 0x8d, 0x87, + 0xce, 0xb2, 0x35, 0x31, 0x61, + 0x74, 0x0a, 0xac, 0xf1, 0xf7, + 0x16, 0x36, 0x47, 0x98, 0x4b, + 0x52, 0x2a, 0x84, 0x8d, 0xf1, + 0xc3, + + 0xa3, 0x01, 0x04, 0x02, 0x58, + 0x24, 0x30, 0x31, 0x38, 0x63, + 0x30, 0x61, 0x65, 0x35, 0x2d, + 0x34, 0x64, 0x39, 0x62, 0x2d, + 0x34, 0x37, 0x31, 0x62, 0x2d, + 0x62, 0x66, 0x64, 0x36, 0x2d, + 0x65, 0x65, 0x66, 0x33, 0x31, + 0x34, 0x62, 0x63, 0x37, 0x30, + 0x33, 0x37, 0x04, 0x58, 0x20, + 0x84, 0x9b, 0x57, 0x21, 0x9d, + 0xae, 0x48, 0xde, 0x64, 0x6d, + 0x07, 0xdb, 0xb5, 0x33, 0x56, + 0x6e, 0x97, 0x66, 0x86, 0x45, + 0x7c, 0x14, 0x91, 0xbe, 0x3a, + 0x76, 0xdc, 0xea, 0x6c, 0x42, + 0x71, 0x88, + + /* kid "sec-48" for hmac 384 */ + + 0xa3, 0x01, 0x04, 0x02, 0x46, + 0x73, 0x65, 0x63, 0x2d, 0x34, + 0x38, 0x20, 0x58, 0x30, 0x84, + 0x9b, 0x57, 0x21, 0x9d, 0xae, + 0x48, 0xde, 0x64, 0x6d, 0x07, + 0xdb, 0xb5, 0x33, 0x56, 0x6e, + 0x97, 0x66, 0x86, 0x45, 0x7c, + 0x14, 0x91, 0xbe, 0x3a, 0x76, + 0xdc, 0xea, 0x6c, 0x42, 0x71, + 0x88, 0x00, 0x11, 0x22, 0x33, + 0x77, 0x88, 0x99, 0xaa, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, + + /* kid "sec-64" for hmac 512 */ + + 0xa3, 0x01, 0x04, 0x02, 0x46, + 0x73, 0x65, 0x63, 0x2d, 0x36, + 0x34, 0x20, 0x58, 0x40, 0x84, + 0x9b, 0x57, 0x21, 0x9d, 0xae, + 0x48, 0xde, 0x64, 0x6d, 0x07, + 0xdb, 0xb5, 0x33, 0x56, 0x6e, + 0x97, 0x66, 0x86, 0x45, 0x7c, + 0x14, 0x91, 0xbe, 0x3a, 0x76, + 0xdc, 0xea, 0x6c, 0x42, 0x71, + 0x88, 0x00, 0x11, 0x22, 0x33, + 0x77, 0x88, 0x99, 0xaa, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0xaa, 0xbb, 0xcc, + 0xdd, 0xee, 0xff, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xa0, 0xb1, + 0xb2, 0xb3, 0xb4, +} +; + +struct keyinfo { + const uint8_t *set; + size_t len; +}; + +struct keyinfo keyset1 = { cose_key_set1, sizeof(cose_key_set1) }, + key3 = { cose_key3, sizeof(cose_key3) }, + key8 = { cose_key8, sizeof(cose_key8) }, + key9 = { cose_key9, sizeof(cose_key9) }, + key10 = { cose_key10, sizeof(cose_key10) } +; + +/* key pieces */ + +static const uint8_t + key1_x[] = { 0xba, 0xc5, 0xb1, 0x1c, 0xad, + 0x8f, 0x99, 0xf9, 0xc7, 0x2b, + 0x05, 0xcf, 0x4b, 0x9e, 0x26, + 0xd2, 0x44, 0xdc, 0x18, 0x9f, + 0x74, 0x52, 0x28, 0x25, 0x5a, + 0x21, 0x9a, 0x86, 0xd6, 0xa0, + 0x9e, 0xff }, + key1_y[] = { 0x20, 0x13, 0x8b, 0xf8, 0x2d, + 0xc1, 0xb6, 0xd5, 0x62, 0xbe, + 0x0f, 0xa5, 0x4a, 0xb7, 0x80, + 0x4a, 0x3a, 0x64, 0xb6, 0xd7, + 0x2c, 0xcf, 0xed, 0x6b, 0x6f, + 0xb6, 0xed, 0x28, 0xbb, 0xfc, + 0x11, 0x7e }, + key1_d[] = { 0x57, 0xc9, 0x20, 0x77, 0x66, + 0x41, 0x46, 0xe8, 0x76, 0x76, + 0x0c, 0x95, 0x20, 0xd0, 0x54, + 0xaa, 0x93, 0xc3, 0xaf, 0xb0, + 0x4e, 0x30, 0x67, 0x05, 0xdb, + 0x60, 0x90, 0x30, 0x85, 0x07, + 0xb4, 0xd3 }, + + key2_x[] = { 0x65, 0xed, 0xa5, 0xa1, 0x25, + 0x77, 0xc2, 0xba, 0xe8, 0x29, + 0x43, 0x7f, 0xe3, 0x38, 0x70, + 0x1a, 0x10, 0xaa, 0xa3, 0x75, + 0xe1, 0xbb, 0x5b, 0x5d, 0xe1, + 0x08, 0xde, 0x43, 0x9c, 0x08, + 0x55, 0x1d }, + key2_y[] = { 0x1e, 0x52, 0xed, 0x75, 0x70, + 0x11, 0x63, 0xf7, 0xf9, 0xe4, + 0x0d, 0xdf, 0x9f, 0x34, 0x1b, + 0x3d, 0xc9, 0xba, 0x86, 0x0a, + 0xf7, 0xe0, 0xca, 0x7c, 0xa7, + 0xe9, 0xee, 0xcd, 0x00, 0x84, + 0xd1, 0x9c }, + key2_d[] = { 0xaf, 0xf9, 0x07, 0xc9, 0x9f, + 0x9a, 0xd3, 0xaa, 0xe6, 0xc4, + 0xcd, 0xf2, 0x11, 0x22, 0xbc, + 0xe2, 0xbd, 0x68, 0xb5, 0x28, + 0x3e, 0x69, 0x07, 0x15, 0x4a, + 0xd9, 0x11, 0x84, 0x0f, 0xa2, + 0x08, 0xcf }, + + key3_k[] = { 0x84, 0x9b, 0x57, 0x21, 0x9d, + 0xae, 0x48, 0xde, 0x64, 0x6d, + 0x07, 0xdb, 0xb5, 0x33, 0x56, + 0x6e, 0x97, 0x66, 0x86, 0x45, + 0x7c, 0x14, 0x91, 0xbe, 0x3a, + 0x76, 0xdc, 0xea, 0x6c, 0x42, + 0x71, 0x88 }, + + key4_x[] = { 0x00, 0x72, 0x99, 0x2c, 0xb3, + 0xac, 0x08, 0xec, 0xf3, 0xe5, + 0xc6, 0x3d, 0xed, 0xec, 0x0d, + 0x51, 0xa8, 0xc1, 0xf7, 0x9e, + 0xf2, 0xf8, 0x2f, 0x94, 0xf3, + 0xc7, 0x37, 0xbf, 0x5d, 0xe7, + 0x98, 0x66, 0x71, 0xea, 0xc6, + 0x25, 0xfe, 0x82, 0x57, 0xbb, + 0xd0, 0x39, 0x46, 0x44, 0xca, + 0xaa, 0x3a, 0xaf, 0x8f, 0x27, + 0xa4, 0x58, 0x5f, 0xbb, 0xca, + 0xd0, 0xf2, 0x45, 0x76, 0x20, + 0x08, 0x5e, 0x5c, 0x8f, 0x42, + 0xad }, + key4_y[] = { 0x01, 0xdc, 0xa6, 0x94, 0x7b, + 0xce, 0x88, 0xbc, 0x57, 0x90, + 0x48, 0x5a, 0xc9, 0x74, 0x27, + 0x34, 0x2b, 0xc3, 0x5f, 0x88, + 0x7d, 0x86, 0xd6, 0x5a, 0x08, + 0x93, 0x77, 0xe2, 0x47, 0xe6, + 0x0b, 0xaa, 0x55, 0xe4, 0xe8, + 0x50, 0x1e, 0x2a, 0xda, 0x57, + 0x24, 0xac, 0x51, 0xd6, 0x90, + 0x90, 0x08, 0x03, 0x3e, 0xbc, + 0x10, 0xac, 0x99, 0x9b, 0x9d, + 0x7f, 0x5c, 0xc2, 0x51, 0x9f, + 0x3f, 0xe1, 0xea, 0x1d, 0x94, + 0x75 }, + key4_d[] = { 0x00, 0x08, 0x51, 0x38, 0xdd, + 0xab, 0xf5, 0xca, 0x97, 0x5f, + 0x58, 0x60, 0xf9, 0x1a, 0x08, + 0xe9, 0x1d, 0x6d, 0x5f, 0x9a, + 0x76, 0xad, 0x40, 0x18, 0x76, + 0x6a, 0x47, 0x66, 0x80, 0xb5, + 0x5c, 0xd3, 0x39, 0xe8, 0xab, + 0x6c, 0x72, 0xb5, 0xfa, 0xcd, + 0xb2, 0xa2, 0xa5, 0x0a, 0xc2, + 0x5b, 0xd0, 0x86, 0x64, 0x7d, + 0xd3, 0xe2, 0xe6, 0xe9, 0x9e, + 0x84, 0xca, 0x2c, 0x36, 0x09, + 0xfd, 0xf1, 0x77, 0xfe, 0xb2, + 0x6d }, + key5_k[] = { 0x84, 0x9b, 0x57, 0x86, 0x45, + 0x7c, 0x14, 0x91, 0xbe, 0x3a, + 0x76, 0xdc, 0xea, 0x6c, 0x42, + 0x71 }, + + key6_x[] = { 0x98, 0xf5, 0x0a, 0x4f, 0xf6, + 0xc0, 0x58, 0x61, 0xc8, 0x86, + 0x0d, 0x13, 0xa6, 0x38, 0xea, + 0x56, 0xc3, 0xf5, 0xad, 0x75, + 0x90, 0xbb, 0xfb, 0xf0, 0x54, + 0xe1, 0xc7, 0xb4, 0xd9, 0x1d, + 0x62, 0x80 }, + key6_y[] = { 0xf0, 0x14, 0x00, 0xb0, 0x89, + 0x86, 0x78, 0x04, 0xb8, 0xe9, + 0xfc, 0x96, 0xc3, 0x93, 0x21, + 0x61, 0xf1, 0x93, 0x4f, 0x42, + 0x23, 0x06, 0x91, 0x70, 0xd9, + 0x24, 0xb7, 0xe0, 0x3b, 0xf8, + 0x22, 0xbb }, + key6_d[] = { 0x02, 0xd1, 0xf7, 0xe6, 0xf2, + 0x6c, 0x43, 0xd4, 0x86, 0x8d, + 0x87, 0xce, 0xb2, 0x35, 0x31, + 0x61, 0x74, 0x0a, 0xac, 0xf1, + 0xf7, 0x16, 0x36, 0x47, 0x98, + 0x4b, 0x52, 0x2a, 0x84, 0x8d, + 0xf1, 0xc3 }, + + key7_k[] = { 0x84, 0x9b, 0x57, 0x21, 0x9d, + 0xae, 0x48, 0xde, 0x64, 0x6d, + 0x07, 0xdb, 0xb5, 0x33, 0x56, + 0x6e, 0x97, 0x66, 0x86, 0x45, + 0x7c, 0x14, 0x91, 0xbe, 0x3a, + 0x76, 0xdc, 0xea, 0x6c, 0x42, + 0x71, 0x88 }, + + key8_k[] = { 0x84, 0x9b, 0x57, 0x21, 0x9d, + 0xae, 0x48, 0xde, 0x64, 0x6d, + 0x07, 0xdb, 0xb5, 0x33, 0x56, + 0x6e, 0x97, 0x66, 0x86, 0x45, + 0x7c, 0x14, 0x91, 0xbe, 0x3a, + 0x76, 0xdc, 0xea, 0x6c, 0x42, + 0x71, 0x88, 0x00, 0x11, 0x22, + 0x33, 0x77, 0x88, 0x99, 0xaa, + 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28 }, + + key9_k[] = { 0x84, 0x9b, 0x57, 0x21, 0x9d, + 0xae, 0x48, 0xde, 0x64, 0x6d, + 0x07, 0xdb, 0xb5, 0x33, 0x56, + 0x6e, 0x97, 0x66, 0x86, 0x45, + 0x7c, 0x14, 0x91, 0xbe, 0x3a, + 0x76, 0xdc, 0xea, 0x6c, 0x42, + 0x71, 0x88, 0x00, 0x11, 0x22, + 0x33, 0x77, 0x88, 0x99, 0xaa, + 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0xaa, 0xbb, + 0xcc, 0xdd, 0xee, 0xff, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xa0, + 0xb1, 0xb2, 0xb3, 0xb4 } +#if 0 + , + key10_x[] = { + 0xd7, 0x5a, 0x98, 0x01, 0x82, + 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, + 0xfe, 0xd3, 0xc9, 0x64, 0x07, + 0x3a, 0x0e, 0xe1, 0x72, 0xf3, + 0xda, 0xa6, 0x23, 0x25, 0xaf, + 0x02, 0x1a, 0x68, 0xf7, 0x07, + 0x51, 0x1a + }, key10_d[] = { + 0x9d, 0x61, 0xb1, 0x9d, 0xef, + 0xfd, 0x5a, 0x60, 0xba, 0x84, + 0x4a, 0xf4, 0x92, 0xec, 0x2c, + 0xc4, 0x44, 0x49, 0xc5, 0x69, + 0x7b, 0x32, 0x69, 0x19, 0x70, + 0x3b, 0xac, 0x03, 0x1c, 0xae, + 0x7f, 0x60 + } +#endif +; + +int +test_cose_keys(struct lws_context *context) +{ + struct lws_cose_key *ck; + lws_dll2_owner_t set; + lws_lec_pctx_t wc; + uint8_t buf[4096]; + int n; + +#if 0 + { + int fd = open("set1.cks", + LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600); + + if (fd >= 0) { + write(fd, cose_key_set1, sizeof(cose_key_set1)); + close(fd); + } + } +#endif + +#if 0 + lws_lec_pctx_t wx; + uint8_t dump[8192]; + + lws_lec_init(&wx, buf, sizeof(buf)); + + if (lws_lec_printf(&wx, + "{%d:%d, %d:%.*b, %d:%d, %d:%.*b, %d:%.*b}", + LWSCOSE_WKK_KTY, LWSCOSE_WKKTV_OKP, + LWSCOSE_WKK_KID, 2, "11", + LWSCOSE_WKOKP_CRV, LWSCOSE_WKEC_ED25519, + LWSCOSE_WKECKP_X, (int)sizeof(key10_x), key10_x, +// LWSCOSE_WKECKP_Y, (int)sizeof(key6_y), key6_y, + LWSCOSE_WKECKP_D, (int)sizeof(key10_d), key10_d) != + LWS_LECPCTX_RET_FINISHED) + return 1; + + lws_hex_from_byte_array(buf, wx.used, (char *)dump, sizeof(dump)); + puts((const char *)dump); +#endif +#if 0 + lws_lec_pctx_t wx; + uint8_t dump[8192]; + + lws_lec_init(&wx, buf, sizeof(buf)); + + if (lws_lec_printf(&wx, + "{%d:%d, %d:%.*b, %d:%.*b}", + LWSCOSE_WKK_KTY, LWSCOSE_WKKTV_SYMMETRIC, + LWSCOSE_WKK_KID, 6, "sec-64", + -1, (int)sizeof(key9_k), key9_k) != + LWS_LECPCTX_RET_FINISHED) + return 1; + + lws_hex_from_byte_array(buf, wx.used, (char *)dump, sizeof(dump)); + puts((const char *)dump); +#endif + + /* key1 import */ + + lwsl_user("%s: key 1 import\n", __func__); + + ck = lws_cose_key_import(NULL, key_import_cb, NULL, cose_key1, sizeof(cose_key1)); + if (!ck) + return 1; + + if (ck->kty != LWSCOSE_WKKTV_EC2 || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_EC || + ck->e[LWS_GENCRYPTO_EC_KEYEL_X].len != sizeof(key1_x) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != sizeof(key1_y) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_D].len != sizeof(key1_d) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_X].buf, key1_x, sizeof(key1_x)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf, key1_y, sizeof(key1_y)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, key1_d, sizeof(key1_d))) + goto bail; + + // lws_cose_key_dump(ck); + + /* key 1 export */ + + lwsl_user("%s: key 1 export\n", __func__); + + lws_lec_init(&wc, buf, sizeof(buf)); + n = (int)lws_cose_key_export(ck, &wc, LWSJWKF_EXPORT_PRIVATE); + lws_cose_key_destroy(&ck); + if (n != LWS_LECPCTX_RET_FINISHED) + goto bail; + + // lwsl_hexdump_notice(buf, wc.used); + + /* key2 import */ + + lwsl_user("%s: key 2 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key2, sizeof(cose_key2)); + if (!ck) + return 1; + + if (ck->kty != LWSCOSE_WKKTV_EC2 || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_EC || + ck->e[LWS_GENCRYPTO_EC_KEYEL_X].len != sizeof(key2_x) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != sizeof(key2_y) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_D].len != sizeof(key2_d) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_X].buf, key2_x, sizeof(key2_x)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf, key2_y, sizeof(key2_y)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, key2_d, sizeof(key2_d))) + goto bail; + + lws_cose_key_destroy(&ck); + + /* key3 import */ + + lwsl_user("%s: key 3 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key3, sizeof(cose_key3)); + if (!ck) { + lwsl_err("%s: key 3 import failed\n", __func__); + goto bail; + } + + if (ck->kty != LWSCOSE_WKKTV_SYMMETRIC || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_OCT || + ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].len != sizeof(key3_k) || + memcmp(ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, key3_k, sizeof(key3_k))) { + lwsl_err("%s: key 3 checks failed %d %d %d\n", __func__, + (int)ck->kty, (int)ck->gencrypto_kty, + (int)ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].len); + goto bail; + } + + lws_cose_key_destroy(&ck); + + /* key4 import */ + + lwsl_user("%s: key 4 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key4, sizeof(cose_key4)); + if (!ck) + return 1; + + if (ck->kty != LWSCOSE_WKKTV_EC2 || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_EC || + ck->e[LWS_GENCRYPTO_EC_KEYEL_X].len != sizeof(key4_x) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != sizeof(key4_y) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_D].len != sizeof(key4_d) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_X].buf, key4_x, sizeof(key4_x)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf, key4_y, sizeof(key4_y)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, key4_d, sizeof(key4_d))) + goto bail; + + lws_cose_key_destroy(&ck); + + /* key5 import */ + + lwsl_user("%s: key 5 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key5, sizeof(cose_key5)); + if (!ck) + return 1; + + if (ck->kty != LWSCOSE_WKKTV_SYMMETRIC || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_OCT || + ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].len != sizeof(key5_k) || + memcmp(ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, key5_k, sizeof(key5_k))) + goto bail; + + lws_cose_key_destroy(&ck); + + /* key6 import */ + + lwsl_user("%s: key 6 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key6, sizeof(cose_key6)); + if (!ck) + return 1; + + if (ck->kty != LWSCOSE_WKKTV_EC2 || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_EC || + ck->e[LWS_GENCRYPTO_EC_KEYEL_X].len != sizeof(key6_x) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != sizeof(key6_y) || + ck->e[LWS_GENCRYPTO_EC_KEYEL_D].len != sizeof(key6_d) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_X].buf, key6_x, sizeof(key6_x)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf, key6_y, sizeof(key6_y)) || + memcmp(ck->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, key6_d, sizeof(key6_d))) + goto bail; + + lws_cose_key_destroy(&ck); + + /* key7 import */ + + lwsl_user("%s: key 7 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key7, sizeof(cose_key7)); + if (!ck) + return 1; + + if (ck->kty != LWSCOSE_WKKTV_SYMMETRIC || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_OCT || + ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].len != sizeof(key7_k) || + memcmp(ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, key7_k, sizeof(key7_k))) + goto bail; + + lws_cose_key_destroy(&ck); + + /* key8 import */ + + lwsl_user("%s: key 8 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key8, sizeof(cose_key8)); + if (!ck) + return 1; + + if (ck->kty != LWSCOSE_WKKTV_SYMMETRIC || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_OCT || + ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].len != sizeof(key8_k) || + memcmp(ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, key8_k, sizeof(key8_k))) + goto bail; + + lws_cose_key_destroy(&ck); + + /* key9 import */ + + lwsl_user("%s: key 9 import\n", __func__); + + ck = lws_cose_key_import(NULL, NULL, NULL, cose_key9, sizeof(cose_key9)); + if (!ck) { + lwsl_err("%s: cose9 import fail\n", __func__); + goto bail; + } + + if (ck->kty != LWSCOSE_WKKTV_SYMMETRIC || + ck->gencrypto_kty != LWS_GENCRYPTO_KTY_OCT || + ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].len != sizeof(key9_k) || + memcmp(ck->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, key9_k, sizeof(key9_k))) { + lwsl_notice("%s: key9 check fails\n", __func__); + goto bail; + } + + lws_cose_key_destroy(&ck); + + /* key set 1 */ + + lwsl_user("%s: key_set1\n", __func__); + lws_dll2_owner_clear(&set); + ck = lws_cose_key_import(&set, NULL, NULL, cose_key_set1, sizeof(cose_key_set1)); + if (!ck) + return 1; + + lws_cose_key_set_destroy(&set); + + /* generate */ + + ck = lws_cose_key_generate(context, LWSCOSE_WKKTV_EC2, + (1 << LWSCOSE_WKKO_SIGN) | + (1 << LWSCOSE_WKKO_VERIFY) | + (1 << LWSCOSE_WKKO_ENCRYPT) | + (1 << LWSCOSE_WKKO_DECRYPT), + 0, "P-256", (const uint8_t *)"the-keyid", 9); + if (!ck) + return 1; + + // lws_cose_key_dump(ck); + + lws_cose_key_destroy(&ck); + + return 0; + +bail: + lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__); + + return 1; +} diff --git a/minimal-examples/api-tests/api-test-cose/main.c b/minimal-examples/api-tests/api-test-cose/main.c new file mode 100644 index 000000000..19de29c50 --- /dev/null +++ b/minimal-examples/api-tests/api-test-cose/main.c @@ -0,0 +1,50 @@ +/* + * lws-api-test-cose + * + * Written in 2010-2021 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include + +int +test_cose_keys(struct lws_context *context); +int +test_cose_sign(struct lws_context *context); + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + const char *p; + int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS COSE api tests\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ +#if defined(LWS_WITH_NETWORK) + info.port = CONTEXT_PORT_NO_LISTEN; +#endif + info.options = 0; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + result |= test_cose_keys(context); + result |= test_cose_sign(context); + + lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS"); + + lws_context_destroy(context); + + return result; +} diff --git a/minimal-examples/api-tests/api-test-cose/sign.c b/minimal-examples/api-tests/api-test-cose/sign.c new file mode 100644 index 000000000..6c8c50b65 --- /dev/null +++ b/minimal-examples/api-tests/api-test-cose/sign.c @@ -0,0 +1,1862 @@ +/* + * lws-api-test-jose - RFC8152 cose_sign tests + * + * Written in 2010-2021 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * Raw key CBOR created from descriptions at + * + * https://github.com/cose-wg/Examples/blob/master/KeySet.txt + */ + +#include +#include +#include + +static const uint8_t + sign1_pass_01[] = { + /* + * https://github.com/cose-wg/Examples/blob/master/ + * sign1-tests/sign-pass-01.json + */ + 0xd2, 0x84, 0x41, 0xa0, 0xa2, + 0x01, 0x26, 0x04, 0x42, 0x31, + 0x31, 0x54, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2e, 0x58, 0x40, 0x87, + 0xdb, 0x0d, 0x2e, 0x55, 0x71, + 0x84, 0x3b, 0x78, 0xac, 0x33, + 0xec, 0xb2, 0x83, 0x0d, 0xf7, + 0xb6, 0xe0, 0xa4, 0xd5, 0xb7, + 0x37, 0x6d, 0xe3, 0x36, 0xb2, + 0x3c, 0x59, 0x1c, 0x90, 0xc4, + 0x25, 0x31, 0x7e, 0x56, 0x12, + 0x7f, 0xbe, 0x04, 0x37, 0x00, + 0x97, 0xce, 0x34, 0x70, 0x87, + 0xb2, 0x33, 0xbf, 0x72, 0x2b, + 0x64, 0x07, 0x2b, 0xeb, 0x44, + 0x86, 0xbd, 0xa4, 0x03, 0x1d, + 0x27, 0x24, 0x4f }, + sign1_pass_02[] = { + 0xd2, 0x84, 0x43, 0xa1, 0x01, + 0x26, 0xa1, 0x04, 0x42, 0x31, + 0x31, 0x54, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2e, 0x58, 0x40, 0x10, + 0x72, 0x9c, 0xd7, 0x11, 0xcb, + 0x38, 0x13, 0xd8, 0xd8, 0xe9, + 0x44, 0xa8, 0xda, 0x71, 0x11, + 0xe7, 0xb2, 0x58, 0xc9, 0xbd, + 0xca, 0x61, 0x35, 0xf7, 0xae, + 0x1a, 0xdb, 0xee, 0x95, 0x09, + 0x89, 0x12, 0x67, 0x83, 0x7e, + 0x1e, 0x33, 0xbd, 0x36, 0xc1, + 0x50, 0x32, 0x6a, 0xe6, 0x27, + 0x55, 0xc6, 0xbd, 0x8e, 0x54, + 0x0c, 0x3e, 0x8f, 0x92, 0xd7, + 0xd2, 0x25, 0xe8, 0xdb, 0x72, + 0xb8, 0x82, 0x0b }, + + sign1_pass_02_ext[] = { + 0x11, 0xaa, 0x22, 0xbb, 0x33, + 0xcc, 0x44, 0xdd, 0x55, 0x00, + 0x66, 0x99 }, + + sign1_pass_03[] = { + 0x84, 0x43, 0xa1, 0x01, 0x26, + 0xa1, 0x04, 0x42, 0x31, 0x31, + 0x54, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2e, 0x58, 0x40, 0x8e, 0xb3, + 0x3e, 0x4c, 0xa3, 0x1d, 0x1c, + 0x46, 0x5a, 0xb0, 0x5a, 0xac, + 0x34, 0xcc, 0x6b, 0x23, 0xd5, + 0x8f, 0xef, 0x5c, 0x08, 0x31, + 0x06, 0xc4, 0xd2, 0x5a, 0x91, + 0xae, 0xf0, 0xb0, 0x11, 0x7e, + 0x2a, 0xf9, 0xa2, 0x91, 0xaa, + 0x32, 0xe1, 0x4a, 0xb8, 0x34, + 0xdc, 0x56, 0xed, 0x2a, 0x22, + 0x34, 0x44, 0x54, 0x7e, 0x01, + 0xf1, 0x1d, 0x3b, 0x09, 0x16, + 0xe5, 0xa4, 0xc3, 0x45, 0xca, + 0xcb, 0x36 }, + sign1_fail_01[] = { + 0xd9, 0x03, 0xe6, 0x84, 0x43, + 0xa1, 0x01, 0x26, 0xa1, 0x04, + 0x42, 0x31, 0x31, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x40, 0x8e, 0xb3, 0x3e, 0x4c, + 0xa3, 0x1d, 0x1c, 0x46, 0x5a, + 0xb0, 0x5a, 0xac, 0x34, 0xcc, + 0x6b, 0x23, 0xd5, 0x8f, 0xef, + 0x5c, 0x08, 0x31, 0x06, 0xc4, + 0xd2, 0x5a, 0x91, 0xae, 0xf0, + 0xb0, 0x11, 0x7e, 0x2a, 0xf9, + 0xa2, 0x91, 0xaa, 0x32, 0xe1, + 0x4a, 0xb8, 0x34, 0xdc, 0x56, + 0xed, 0x2a, 0x22, 0x34, 0x44, + 0x54, 0x7e, 0x01, 0xf1, 0x1d, + 0x3b, 0x09, 0x16, 0xe5, 0xa4, + 0xc3, 0x45, 0xca, 0xcb, 0x36 }, + sign1_fail_02[] = { + 0xd2, 0x84, 0x43, 0xa1, 0x01, + 0x26, 0xa1, 0x04, 0x42, 0x31, + 0x31, 0x54, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2f, 0x58, 0x40, 0x8e, + 0xb3, 0x3e, 0x4c, 0xa3, 0x1d, + 0x1c, 0x46, 0x5a, 0xb0, 0x5a, + 0xac, 0x34, 0xcc, 0x6b, 0x23, + 0xd5, 0x8f, 0xef, 0x5c, 0x08, + 0x31, 0x06, 0xc4, 0xd2, 0x5a, + 0x91, 0xae, 0xf0, 0xb0, 0x11, + 0x7e, 0x2a, 0xf9, 0xa2, 0x91, + 0xaa, 0x32, 0xe1, 0x4a, 0xb8, + 0x34, 0xdc, 0x56, 0xed, 0x2a, + 0x22, 0x34, 0x44, 0x54, 0x7e, + 0x01, 0xf1, 0x1d, 0x3b, 0x09, + 0x16, 0xe5, 0xa4, 0xc3, 0x45, + 0xca, 0xcb, 0x36 }, + sign1_fail_03[] = { + 0xd2, 0x84, 0x45, 0xa1, 0x01, + 0x39, 0x03, 0xe6, 0xa1, 0x04, + 0x42, 0x31, 0x31, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x40, 0x8e, 0xb3, 0x3e, 0x4c, + 0xa3, 0x1d, 0x1c, 0x46, 0x5a, + 0xb0, 0x5a, 0xac, 0x34, 0xcc, + 0x6b, 0x23, 0xd5, 0x8f, 0xef, + 0x5c, 0x08, 0x31, 0x06, 0xc4, + 0xd2, 0x5a, 0x91, 0xae, 0xf0, + 0xb0, 0x11, 0x7e, 0x2a, 0xf9, + 0xa2, 0x91, 0xaa, 0x32, 0xe1, + 0x4a, 0xb8, 0x34, 0xdc, 0x56, + 0xed, 0x2a, 0x22, 0x34, 0x44, + 0x54, 0x7e, 0x01, 0xf1, 0x1d, + 0x3b, 0x09, 0x16, 0xe5, 0xa4, + 0xc3, 0x45, 0xca, 0xcb, 0x36 }, + sign1_fail_04[] = { + 0xd2, 0x84, 0x4a, 0xa1, 0x01, + 0x67, 0x75, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0xa1, 0x04, + 0x42, 0x31, 0x31, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x40, 0x8e, 0xb3, 0x3e, 0x4c, + 0xa3, 0x1d, 0x1c, 0x46, 0x5a, + 0xb0, 0x5a, 0xac, 0x34, 0xcc, + 0x6b, 0x23, 0xd5, 0x8f, 0xef, + 0x5c, 0x08, 0x31, 0x06, 0xc4, + 0xd2, 0x5a, 0x91, 0xae, 0xf0, + 0xb0, 0x11, 0x7e, 0x2a, 0xf9, + 0xa2, 0x91, 0xaa, 0x32, 0xe1, + 0x4a, 0xb8, 0x34, 0xdc, 0x56, + 0xed, 0x2a, 0x22, 0x34, 0x44, + 0x54, 0x7e, 0x01, 0xf1, 0x1d, + 0x3b, 0x09, 0x16, 0xe5, 0xa4, + 0xc3, 0x45, 0xca, 0xcb, 0x36 }, + + /* sign1/fail05 is missing upstream */ + + sign1_fail_06[] = { + 0xd2, 0x84, 0x45, 0xa2, 0x01, + 0x26, 0x03, 0x00, 0xa1, 0x04, + 0x42, 0x31, 0x31, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x40, 0x8e, 0xb3, 0x3e, 0x4c, + 0xa3, 0x1d, 0x1c, 0x46, 0x5a, + 0xb0, 0x5a, 0xac, 0x34, 0xcc, + 0x6b, 0x23, 0xd5, 0x8f, 0xef, + 0x5c, 0x08, 0x31, 0x06, 0xc4, + 0xd2, 0x5a, 0x91, 0xae, 0xf0, + 0xb0, 0x11, 0x7e, 0x2a, 0xf9, + 0xa2, 0x91, 0xaa, 0x32, 0xe1, + 0x4a, 0xb8, 0x34, 0xdc, 0x56, + 0xed, 0x2a, 0x22, 0x34, 0x44, + 0x54, 0x7e, 0x01, 0xf1, 0x1d, + 0x3b, 0x09, 0x16, 0xe5, 0xa4, + 0xc3, 0x45, 0xca, 0xcb, 0x36 }, + + sign1_fail_07[] = { + 0xd2, 0x84, 0x43, 0xa1, 0x01, + 0x26, 0xa1, 0x04, 0x42, 0x31, + 0x31, 0x54, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2e, 0x58, 0x40, 0x65, + 0x20, 0xbb, 0xaf, 0x20, 0x81, + 0xd7, 0xe0, 0xed, 0x0f, 0x95, + 0xf7, 0x6e, 0xb0, 0x73, 0x3d, + 0x66, 0x70, 0x05, 0xf7, 0x46, + 0x7c, 0xec, 0x4b, 0x87, 0xb9, + 0x38, 0x1a, 0x6b, 0xa1, 0xed, + 0xe8, 0xe0, 0x0d, 0xf2, 0x9f, + 0x32, 0xa3, 0x72, 0x30, 0xf3, + 0x9a, 0x84, 0x2a, 0x54, 0x82, + 0x1f, 0xdd, 0x22, 0x30, 0x92, + 0x81, 0x9d, 0x77, 0x28, 0xef, + 0xb9, 0xd3, 0xa0, 0x08, 0x0b, + 0x75, 0x38, 0x0b }, + + sign_pass_01[] = { + 0xd8, 0x62, 0x84, 0x41, 0xa0, + 0xa0, 0x54, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2e, 0x81, 0x83, 0x43, + 0xa1, 0x01, 0x26, 0xa1, 0x04, + 0x42, 0x31, 0x31, 0x58, 0x40, + 0xe2, 0xae, 0xaf, 0xd4, 0x0d, + 0x69, 0xd1, 0x9d, 0xfe, 0x6e, + 0x52, 0x07, 0x7c, 0x5d, 0x7f, + 0xf4, 0xe4, 0x08, 0x28, 0x2c, + 0xbe, 0xfb, 0x5d, 0x06, 0xcb, + 0xf4, 0x14, 0xaf, 0x2e, 0x19, + 0xd9, 0x82, 0xac, 0x45, 0xac, + 0x98, 0xb8, 0x54, 0x4c, 0x90, + 0x8b, 0x45, 0x07, 0xde, 0x1e, + 0x90, 0xb7, 0x17, 0xc3, 0xd3, + 0x48, 0x16, 0xfe, 0x92, 0x6a, + 0x2b, 0x98, 0xf5, 0x3a, 0xfd, + 0x2f, 0xa0, 0xf3, 0x0a }, + + sign_pass_02[] = { + 0xd8, 0x62, 0x84, 0x40, 0xa0, + 0x54, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2e, 0x81, 0x83, 0x43, 0xa1, + 0x01, 0x26, 0xa1, 0x04, 0x42, + 0x31, 0x31, 0x58, 0x40, 0xcb, + 0xb8, 0xda, 0xd9, 0xbe, 0xaf, + 0xb8, 0x90, 0xe1, 0xa4, 0x14, + 0x12, 0x4d, 0x8b, 0xfb, 0xc2, + 0x6b, 0xed, 0xf2, 0xa9, 0x4f, + 0xcb, 0x5a, 0x88, 0x24, 0x32, + 0xbf, 0xf6, 0xd6, 0x3e, 0x15, + 0xf5, 0x74, 0xee, 0xb2, 0xab, + 0x51, 0xd8, 0x3f, 0xa2, 0xcb, + 0xf6, 0x26, 0x72, 0xeb, 0xf4, + 0xc7, 0xd9, 0x93, 0xb0, 0xf4, + 0xc2, 0x44, 0x76, 0x47, 0xd8, + 0x31, 0xba, 0x57, 0xcc, 0xa8, + 0x6b, 0x93, 0x0a }, + + sign_pass_03[] = { + 0x84, 0x40, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x81, + 0x83, 0x43, 0xa1, 0x01, 0x26, + 0xa1, 0x04, 0x42, 0x31, 0x31, + 0x58, 0x40, 0xe2, 0xae, 0xaf, + 0xd4, 0x0d, 0x69, 0xd1, 0x9d, + 0xfe, 0x6e, 0x52, 0x07, 0x7c, + 0x5d, 0x7f, 0xf4, 0xe4, 0x08, + 0x28, 0x2c, 0xbe, 0xfb, 0x5d, + 0x06, 0xcb, 0xf4, 0x14, 0xaf, + 0x2e, 0x19, 0xd9, 0x82, 0xac, + 0x45, 0xac, 0x98, 0xb8, 0x54, + 0x4c, 0x90, 0x8b, 0x45, 0x07, + 0xde, 0x1e, 0x90, 0xb7, 0x17, + 0xc3, 0xd3, 0x48, 0x16, 0xfe, + 0x92, 0x6a, 0x2b, 0x98, 0xf5, + 0x3a, 0xfd, 0x2f, 0xa0, 0xf3, + 0x0a }, + + sign_fail_01[] = { + 0xd9, 0x03, 0xe6, 0x84, 0x40, + 0xa0, 0x54, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2e, 0x81, 0x83, 0x43, + 0xa1, 0x01, 0x26, 0xa1, 0x04, + 0x42, 0x31, 0x31, 0x58, 0x40, + 0xe2, 0xae, 0xaf, 0xd4, 0x0d, + 0x69, 0xd1, 0x9d, 0xfe, 0x6e, + 0x52, 0x07, 0x7c, 0x5d, 0x7f, + 0xf4, 0xe4, 0x08, 0x28, 0x2c, + 0xbe, 0xfb, 0x5d, 0x06, 0xcb, + 0xf4, 0x14, 0xaf, 0x2e, 0x19, + 0xd9, 0x82, 0xac, 0x45, 0xac, + 0x98, 0xb8, 0x54, 0x4c, 0x90, + 0x8b, 0x45, 0x07, 0xde, 0x1e, + 0x90, 0xb7, 0x17, 0xc3, 0xd3, + 0x48, 0x16, 0xfe, 0x92, 0x6a, + 0x2b, 0x98, 0xf5, 0x3a, 0xfd, + 0x2f, 0xa0, 0xf3, 0x0a }, + + sign_fail_02[] = { + 0xd8, 0x62, 0x84, 0x40, 0xa0, + 0x54, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2e, 0x81, 0x83, 0x43, 0xa1, + 0x01, 0x26, 0xa1, 0x04, 0x42, + 0x31, 0x31, 0x58, 0x40, 0xe2, + 0xae, 0xaf, 0xd4, 0x0d, 0x69, + 0xd1, 0x9d, 0xfe, 0x6e, 0x52, + 0x07, 0x7c, 0x5d, 0x7f, 0xf4, + 0xe4, 0x08, 0x28, 0x2c, 0xbe, + 0xfb, 0x5d, 0x06, 0xcb, 0xf4, + 0x14, 0xaf, 0x2e, 0x19, 0xd9, + 0x82, 0xac, 0x45, 0xac, 0x98, + 0xb8, 0x54, 0x4c, 0x90, 0x8b, + 0x45, 0x07, 0xde, 0x1e, 0x90, + 0xb7, 0x17, 0xc3, 0xd3, 0x48, + 0x16, 0xfe, 0x92, 0x6a, 0x2b, + 0x98, 0xf5, 0x3a, 0xfd, 0x2f, + 0xa0, 0xf3, 0x0b }, + + sign_fail_03[] = { + 0xd8, 0x62, 0x84, 0x40, 0xa0, + 0x54, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2e, 0x81, 0x83, 0x45, 0xa1, + 0x01, 0x39, 0x03, 0xe6, 0xa1, + 0x04, 0x42, 0x31, 0x31, 0x58, + 0x40, 0xe2, 0xae, 0xaf, 0xd4, + 0x0d, 0x69, 0xd1, 0x9d, 0xfe, + 0x6e, 0x52, 0x07, 0x7c, 0x5d, + 0x7f, 0xf4, 0xe4, 0x08, 0x28, + 0x2c, 0xbe, 0xfb, 0x5d, 0x06, + 0xcb, 0xf4, 0x14, 0xaf, 0x2e, + 0x19, 0xd9, 0x82, 0xac, 0x45, + 0xac, 0x98, 0xb8, 0x54, 0x4c, + 0x90, 0x8b, 0x45, 0x07, 0xde, + 0x1e, 0x90, 0xb7, 0x17, 0xc3, + 0xd3, 0x48, 0x16, 0xfe, 0x92, + 0x6a, 0x2b, 0x98, 0xf5, 0x3a, + 0xfd, 0x2f, 0xa0, 0xf3, 0x0a }, + + sign_fail_04[] = { + 0xd8, 0x62, 0x84, 0x40, 0xa0, + 0x54, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2e, 0x81, 0x83, 0x4a, 0xa1, + 0x01, 0x67, 0x75, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0xa1, + 0x04, 0x42, 0x31, 0x31, 0x58, + 0x40, 0xe2, 0xae, 0xaf, 0xd4, + 0x0d, 0x69, 0xd1, 0x9d, 0xfe, + 0x6e, 0x52, 0x07, 0x7c, 0x5d, + 0x7f, 0xf4, 0xe4, 0x08, 0x28, + 0x2c, 0xbe, 0xfb, 0x5d, 0x06, + 0xcb, 0xf4, 0x14, 0xaf, 0x2e, + 0x19, 0xd9, 0x82, 0xac, 0x45, + 0xac, 0x98, 0xb8, 0x54, 0x4c, + 0x90, 0x8b, 0x45, 0x07, 0xde, + 0x1e, 0x90, 0xb7, 0x17, 0xc3, + 0xd3, 0x48, 0x16, 0xfe, 0x92, + 0x6a, 0x2b, 0x98, 0xf5, 0x3a, + 0xfd, 0x2f, 0xa0, 0xf3, 0x0a }, + + /* fail 5 missing upstream */ + + sign_fail_06[] = { + 0xd8, 0x62, 0x84, 0x43, 0xa1, + 0x03, 0x00, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x81, + 0x83, 0x43, 0xa1, 0x01, 0x26, + 0xa1, 0x04, 0x42, 0x31, 0x31, + 0x58, 0x40, 0xe2, 0xae, 0xaf, + 0xd4, 0x0d, 0x69, 0xd1, 0x9d, + 0xfe, 0x6e, 0x52, 0x07, 0x7c, + 0x5d, 0x7f, 0xf4, 0xe4, 0x08, + 0x28, 0x2c, 0xbe, 0xfb, 0x5d, + 0x06, 0xcb, 0xf4, 0x14, 0xaf, + 0x2e, 0x19, 0xd9, 0x82, 0xac, + 0x45, 0xac, 0x98, 0xb8, 0x54, + 0x4c, 0x90, 0x8b, 0x45, 0x07, + 0xde, 0x1e, 0x90, 0xb7, 0x17, + 0xc3, 0xd3, 0x48, 0x16, 0xfe, + 0x92, 0x6a, 0x2b, 0x98, 0xf5, + 0x3a, 0xfd, 0x2f, 0xa0, 0xf3, + 0x0a }, + + sign_fail_07[] = { + 0xd8, 0x62, 0x84, 0x41, 0xa0, + 0xa0, 0x54, 0x54, 0x68, 0x69, + 0x73, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2e, 0x81, 0x83, 0x43, + 0xa1, 0x01, 0x26, 0xa1, 0x04, + 0x42, 0x31, 0x31, 0x58, 0x40, + 0xd7, 0x1c, 0x05, 0xdb, 0x52, + 0xc9, 0xce, 0x7f, 0x1b, 0xf5, + 0xaa, 0xc0, 0x13, 0x34, 0xbb, + 0xea, 0xca, 0xc1, 0xd8, 0x6a, + 0x23, 0x03, 0xe6, 0xee, 0xaa, + 0x89, 0x26, 0x6f, 0x45, 0xc0, + 0x1e, 0xd6, 0x02, 0xca, 0x64, + 0x9e, 0xaf, 0x79, 0x0d, 0x8b, + 0xc9, 0x9d, 0x24, 0x58, 0x45, + 0x7c, 0xa6, 0xa8, 0x72, 0x06, + 0x19, 0x40, 0xe7, 0xaf, 0xbe, + 0x48, 0xe2, 0x89, 0xdf, 0xac, + 0x14, 0x6a, 0xe2, 0x58 }, + + sign_hmac_01[] = { + 0xd8, 0x61, 0x85, 0x43, 0xa1, + 0x01, 0x05, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x20, 0x2b, 0xdc, 0xc8, 0x9f, + 0x05, 0x82, 0x16, 0xb8, 0xa2, + 0x08, 0xdd, 0xc6, 0xd8, 0xb5, + 0x4a, 0xa9, 0x1f, 0x48, 0xbd, + 0x63, 0x48, 0x49, 0x86, 0x56, + 0x51, 0x05, 0xc9, 0xad, 0x5a, + 0x66, 0x82, 0xf6, 0x81, 0x83, + 0x40, 0xa2, 0x01, 0x25, 0x04, + 0x4a, 0x6f, 0x75, 0x72, 0x2d, + 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x40 }, + + sign_hmac_02[] = { + 0xd8, 0x61, 0x85, 0x43, 0xa1, + 0x01, 0x06, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x30, 0xb3, 0x09, 0x7f, 0x70, + 0x00, 0x9a, 0x11, 0x50, 0x74, + 0x09, 0x59, 0x8a, 0x83, 0xe1, + 0x5b, 0xbb, 0xbf, 0x19, 0x82, + 0xdc, 0xe2, 0x8e, 0x5a, 0xb6, + 0xd5, 0xa6, 0xaf, 0xf6, 0x89, + 0x7b, 0xd2, 0x4b, 0xb8, 0xb7, + 0x47, 0x96, 0x22, 0xc9, 0x40, + 0x1b, 0x24, 0x09, 0x0d, 0x45, + 0x82, 0x06, 0xd5, 0x87, 0x81, + 0x83, 0x40, 0xa2, 0x01, 0x25, + 0x04, 0x46, 0x73, 0x65, 0x63, + 0x2d, 0x34, 0x38, 0x40 }, + + sign_hmac_03[] = { + 0xd8, 0x61, 0x85, 0x43, 0xa1, + 0x01, 0x07, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x40, 0xcd, 0x28, 0xa6, 0xb3, + 0xcf, 0xbb, 0xbf, 0x21, 0x48, + 0x51, 0xb9, 0x06, 0xe0, 0x50, + 0x05, 0x6c, 0xb4, 0x38, 0xa8, + 0xb8, 0x89, 0x05, 0xb8, 0xb7, + 0x46, 0x19, 0x77, 0x02, 0x27, + 0x11, 0xa9, 0xd8, 0xac, 0x5d, + 0xbc, 0x54, 0xe2, 0x9a, 0x56, + 0xd9, 0x26, 0x04, 0x6b, 0x40, + 0xfc, 0x26, 0x07, 0xc2, 0x5b, + 0x34, 0x44, 0x54, 0xaa, 0x5f, + 0x68, 0xde, 0x09, 0xa3, 0xe5, + 0x25, 0xd3, 0x86, 0x5a, 0x05, + 0x81, 0x83, 0x40, 0xa2, 0x01, + 0x25, 0x04, 0x46, 0x73, 0x65, + 0x63, 0x2d, 0x36, 0x34, 0x40 }, + + sign_hmac_04[] = { + 0xd8, 0x61, 0x85, 0x43, 0xa1, + 0x01, 0x05, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x58, + 0x20, 0x2b, 0xdc, 0xc8, 0x9f, + 0x05, 0x82, 0x16, 0xb8, 0xa2, + 0x08, 0xdd, 0xc6, 0xd8, 0xb5, + 0x4a, 0xa9, 0x1f, 0x48, 0xbd, + 0x63, 0x48, 0x49, 0x86, 0x56, + 0x51, 0x05, 0xc9, 0xad, 0x5a, + 0x66, 0x82, 0xf7, 0x81, 0x83, + 0x40, 0xa2, 0x01, 0x25, 0x04, + 0x4a, 0x6f, 0x75, 0x72, 0x2d, + 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x40 }, + + sign_hmac_05[] = { + 0xd8, 0x61, 0x85, 0x43, 0xa1, + 0x01, 0x04, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x48, + 0x6f, 0x35, 0xca, 0xb7, 0x79, + 0xf7, 0x78, 0x33, 0x81, 0x83, + 0x40, 0xa2, 0x01, 0x25, 0x04, + 0x4a, 0x6f, 0x75, 0x72, 0x2d, + 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x40 }, + + enc_hmac_01[] = { + 0xd1, 0x84, 0x43, 0xa1, 0x01, + 0x05, 0xa0, 0x54, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x2e, 0x58, 0x20, + 0xa1, 0xa8, 0x48, 0xd3, 0x47, + 0x1f, 0x9d, 0x61, 0xee, 0x49, + 0x01, 0x8d, 0x24, 0x4c, 0x82, + 0x47, 0x72, 0xf2, 0x23, 0xad, + 0x4f, 0x93, 0x52, 0x93, 0xf1, + 0x78, 0x9f, 0xc3, 0xa0, 0x8d, + 0x8c, 0x58 }, + + enc_hmac_02[] = { + 0xd1, 0x84, 0x43, 0xa1, 0x01, + 0x06, 0xa0, 0x54, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x2e, 0x58, 0x30, + 0x99, 0x8d, 0x26, 0xc6, 0x45, + 0x9a, 0xae, 0xec, 0xf4, 0x4e, + 0xd2, 0x0c, 0xe0, 0x0c, 0x8c, + 0xce, 0xdf, 0x0a, 0x1f, 0x3d, + 0x22, 0xa9, 0x2f, 0xc0, 0x5d, + 0xb0, 0x8c, 0x5a, 0xeb, 0x1c, + 0xb5, 0x94, 0xca, 0xaf, 0x5a, + 0x5c, 0x5e, 0x2e, 0x9d, 0x01, + 0xcc, 0xe7, 0xe7, 0x7a, 0x93, + 0xaa, 0x8c, 0x62 }, + + enc_hmac_03[] = { + 0xd1, 0x84, 0x43, 0xa1, 0x01, + 0x07, 0xa0, 0x54, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x2e, 0x58, 0x40, + 0x4a, 0x55, 0x5b, 0xf9, 0x71, + 0xf7, 0xc1, 0x89, 0x1d, 0x9d, + 0xdf, 0x30, 0x4a, 0x1a, 0x13, + 0x2e, 0x2d, 0x6f, 0x81, 0x74, + 0x49, 0x47, 0x4d, 0x81, 0x3e, + 0x6d, 0x04, 0xd6, 0x59, 0x62, + 0xbe, 0xd8, 0xbb, 0xa7, 0x0c, + 0x17, 0xe1, 0xf5, 0x30, 0x8f, + 0xa3, 0x99, 0x62, 0x95, 0x9a, + 0x4b, 0x9b, 0x8d, 0x7d, 0xa8, + 0xe6, 0xd8, 0x49, 0xb2, 0x09, + 0xdc, 0xd3, 0xe9, 0x8c, 0xc0, + 0xf1, 0x1e, 0xdd, 0xf2 }, + + enc_hmac_04[] = { + 0xd1, 0x84, 0x43, 0xa1, 0x01, + 0x05, 0xa0, 0x54, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x2e, 0x58, 0x20, + 0xa1, 0xa8, 0x48, 0xd3, 0x47, + 0x1f, 0x9d, 0x61, 0xee, 0x49, + 0x01, 0x8d, 0x24, 0x4c, 0x82, + 0x47, 0x72, 0xf2, 0x23, 0xad, + 0x4f, 0x93, 0x52, 0x93, 0xf1, + 0x78, 0x9f, 0xc3, 0xa0, 0x8d, + 0x8c, 0x59 }, + + enc_hmac_05[] = { + 0xd1, 0x84, 0x43, 0xa1, 0x01, + 0x04, 0xa0, 0x54, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x2e, 0x48, 0x11, + 0xf9, 0xe3, 0x57, 0x97, 0x5f, + 0xb8, 0x49 } +#if 0 +, + + countersign_sign_01[] = { + 0xd8, 0x62, 0x84, 0x43, 0xa1, + 0x03, 0x00, 0xa0, 0x54, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x69, + 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2e, 0x81, + 0x83, 0x43, 0xa1, 0x01, 0x27, + 0xa2, 0x07, 0x83, 0x43, 0xa1, + 0x01, 0x27, 0xa1, 0x04, 0x42, + 0x31, 0x31, 0x58, 0x40, 0x8e, + 0x1b, 0xe2, 0xf9, 0x45, 0x3d, + 0x26, 0x48, 0x12, 0xe5, 0x90, + 0x49, 0x91, 0x32, 0xbe, 0xf3, + 0xfb, 0xf9, 0xee, 0x9d, 0xb2, + 0x7c, 0x2c, 0x16, 0x87, 0x88, + 0xe3, 0xb7, 0xeb, 0xe5, 0x06, + 0xc0, 0x4f, 0xd3, 0xd1, 0x9f, + 0xaa, 0x9f, 0x51, 0x23, 0x2a, + 0xf5, 0xc9, 0x59, 0xe4, 0xef, + 0x47, 0x92, 0x88, 0x34, 0x64, + 0x7f, 0x56, 0xdf, 0xbe, 0x93, + 0x91, 0x12, 0x88, 0x4d, 0x08, + 0xef, 0x25, 0x05, 0x04, 0x42, + 0x31, 0x31, 0x58, 0x40, 0x77, + 0xf3, 0xea, 0xcd, 0x11, 0x85, + 0x2c, 0x4b, 0xf9, 0xcb, 0x1d, + 0x72, 0xfa, 0xbe, 0x6b, 0x26, + 0xfb, 0xa1, 0xd7, 0x60, 0x92, + 0xb2, 0xb5, 0xb7, 0xec, 0x83, + 0xb8, 0x35, 0x57, 0x65, 0x22, + 0x64, 0xe6, 0x96, 0x90, 0xdb, + 0xc1, 0x17, 0x2d, 0xdc, 0x0b, + 0xf8, 0x84, 0x11, 0xc0, 0xd2, + 0x5a, 0x50, 0x7f, 0xdb, 0x24, + 0x7a, 0x20, 0xc4, 0x0d, 0x5e, + 0x24, 0x5f, 0xab, 0xd3, 0xfc, + 0x9e, 0xc1, 0x06 } +#endif +; + +extern const struct { + const uint8_t *set; + size_t len; +} keyset1, key3, key8, key9, key10; + +static int +xcb(lws_cose_sig_ext_pay_t *x) +{ + x->ext = sign1_pass_02_ext; + x->xl = sizeof(sign1_pass_02_ext); + + return LCOSESIGEXTCB_RET_FINISHED; +} + + + +int +test_cose_sign(struct lws_context *context) +{ + struct lws_cose_validate_context *cps; + lws_cose_validate_create_info_t info; + lws_cose_validate_res_t *res; + lws_dll2_owner_t set; + lws_dll2_owner_t *o; + int n; + + memset(&info, 0, sizeof(info)); + info.cx = context; + info.keyset = &set; + +#if 1 + { + int fd = open("sign_hmac01.sig", + LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600); + + if (fd >= 0) { + write(fd, sign_hmac_01, sizeof(sign_hmac_01)); + close(fd); + } + } +#endif + + /* + * valid sign1 we have key for + */ + + lwsl_user("%s: sign1/sign-pass-01\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_pass_01, sizeof(sign1_pass_01), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid sign1 but empty key set, so can't judge it + */ + + lwsl_user("%s: sign1/sign-pass-01 - no key\n", __func__); + + lws_dll2_owner_clear(&set); + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_pass_01, sizeof(sign1_pass_01), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid sign1 + */ + + lwsl_user("%s: sign1/sign-pass-02\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + info.ext_cb = xcb; + info.ext_len = sizeof(sign1_pass_02_ext); + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_pass_02, sizeof(sign1_pass_02), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid sign1 without enclosing tag + */ + + lwsl_user("%s: sign1/sign-pass-03\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + info.ext_cb = NULL; + info.ext_len = 0; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_pass_03, sizeof(sign1_pass_03), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * sign1 with wrong tag + */ + + lwsl_user("%s: sign1/sign-fail-01\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_fail_01, sizeof(sign1_fail_01), + NULL); + if (!n) { + lwsl_notice("%s: sign_val_chunk should have failed\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * invalid sign1, signature tampered + */ + + lwsl_user("%s: sign1/sign-fail-02\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_fail_02, sizeof(sign1_fail_02), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + /* validation result must be fail */ + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * invalid sign1, alg tampered + */ + + lwsl_user("%s: sign1/sign-fail-03\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_fail_03, sizeof(sign1_fail_03), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + /* validation result must be fail */ + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * invalid sign1, alg sign tampered + */ + + lwsl_user("%s: sign1/sign-fail-04\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_fail_04, sizeof(sign1_fail_04), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + /* validation result must be fail */ + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * invalid sign1, protected attributes tampered + */ + + lwsl_user("%s: sign1/sign-fail-06\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_fail_06, sizeof(sign1_fail_06), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + /* validation result must be fail */ + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * invalid sign1, protected attribute removed + */ + + lwsl_user("%s: sign1/sign-fail-07\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_SINGLE; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign1_fail_07, sizeof(sign1_fail_07), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) + goto bail1; + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + /* validation result must be fail */ + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid sign we have key for + */ + + lwsl_user("%s: sign/sign-pass-01\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_pass_01, sizeof(sign_pass_01), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid sign we have key for + */ + + lwsl_user("%s: sign/sign-pass-02\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + info.ext_cb = xcb; + info.ext_len = sizeof(sign1_pass_02_ext); + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_pass_02, sizeof(sign_pass_02), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid sign we have key for + */ + + lwsl_user("%s: sign/sign-pass-03\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + info.ext_cb = NULL; + info.ext_len = 0; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_pass_03, sizeof(sign_pass_03), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * wrong cbor tag + */ + + lwsl_user("%s: sign/sign-fail-01\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_fail_01, sizeof(sign_fail_01), + NULL); + if (!n) { + lwsl_notice("%s: sign_val_chunk should fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * tampered signature + */ + + lwsl_user("%s: sign/sign-fail-02\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_fail_02, sizeof(sign_fail_02), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * tampered sign alg -999 + */ + + lwsl_user("%s: sign/sign-fail-03\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_fail_03, sizeof(sign_fail_03), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * tampered sign alg 0 + */ + + lwsl_user("%s: sign/sign-fail-04\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_fail_04, sizeof(sign_fail_04), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * add protected attribute + */ + + lwsl_user("%s: sign/sign-fail-06\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_fail_06, sizeof(sign_fail_06), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * remove protected attribute + */ + + lwsl_user("%s: sign/sign-fail-07\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MULTI; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_fail_07, sizeof(sign_fail_07), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_notice("%s: results: %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) + goto bail1; + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + + /* + * valid HMAC sign we have key for + */ + + lwsl_user("%s: hmac-examples/hmac-01\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_hmac_01, sizeof(sign_hmac_01), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid HMAC sign we have key for + */ + + lwsl_user("%s: hmac-examples/hmac-02\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_hmac_02, sizeof(sign_hmac_02), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + + /* + * valid HMAC sign we have key for + */ + + lwsl_user("%s: hmac-examples/hmac-03\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_hmac_03, sizeof(sign_hmac_03), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * invalid HMAC sign we have key for + */ + + lwsl_user("%s: hmac-examples/hmac-04 fail mac tag\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_hmac_04, sizeof(sign_hmac_04), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) { + lwsl_err("%s: result is wrongly succeeding\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid HMAC sign we have key for HS256/64 + */ + + lwsl_user("%s: hmac-examples/hmac-05\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, keyset1.set, keyset1.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, sign_hmac_05, sizeof(sign_hmac_05), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid HMAC sign with implicit HS256 key + */ + + lwsl_user("%s: hmac-examples/enc-01\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, key3.set, key3.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC0; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, enc_hmac_01, sizeof(enc_hmac_01), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid HMAC sign with implicit HS384 key + */ + + lwsl_user("%s: hmac-examples/enc-02\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, key8.set, key8.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC0; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, enc_hmac_02, sizeof(enc_hmac_02), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid HMAC sign with implicit HS512 key + */ + + lwsl_user("%s: hmac-examples/enc-03\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, key9.set, key9.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC0; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, enc_hmac_03, sizeof(enc_hmac_03), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * invalid HMAC sign with implicit HS256 key, tampered hmac tag + */ + + lwsl_user("%s: hmac-examples/enc-04\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, key3.set, key3.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC0; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, enc_hmac_04, sizeof(enc_hmac_04), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (!res->result) { + lwsl_err("%s: result wrongly succeeds\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); + + /* + * valid HMAC sign with implicit HS256 key, HS256/64 + */ + + lwsl_user("%s: hmac-examples/enc-05\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, key3.set, key3.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_MAC0; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, enc_hmac_05, sizeof(enc_hmac_05), + NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); +#if 0 + /* + * valid Ed25519 signature with countersignature from same key + alg + */ + + lwsl_user("%s: countersign/sign-01\n", __func__); + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, key10.set, key10.len)) { + lwsl_notice("%s: key import fail\n", __func__); + return 1; + } + + info.sigtype = SIGTYPE_COUNTERSIGNED; + cps = lws_cose_validate_create(&info); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + n = lws_cose_validate_chunk(cps, countersign_sign_01, + sizeof(countersign_sign_01), NULL); + if (n) { + lwsl_notice("%s: sign_val_chunk failed\n", __func__); + goto bail1; + } + + o = lws_cose_validate_results(cps); + if (o->count != 1) { + lwsl_err("%s: result count %d\n", __func__, o->count); + goto bail1; + } + + res = lws_container_of(o->head, lws_cose_validate_res_t, list); + if (res->result) { + lwsl_err("%s: result is fail\n", __func__); + goto bail1; + } + + lws_cose_validate_destroy(&cps); + lws_cose_key_set_destroy(&set); +#endif + + return 0; + +bail1: + lws_cose_validate_destroy(&cps); +bail: + lws_cose_key_set_destroy(&set); + + return 1; +} diff --git a/minimal-examples/api-tests/api-test-lecp/main.c b/minimal-examples/api-tests/api-test-lecp/main.c index 1e6654102..89133d70f 100644 --- a/minimal-examples/api-tests/api-test-lecp/main.c +++ b/minimal-examples/api-tests/api-test-lecp/main.c @@ -50,6 +50,7 @@ static const char * const reason_names[] = { "LECPCB_VAL_BLOB_END", "LECPCB_ARRAY_ITEM_START", "LECPCB_ARRAY_ITEM_END", + "LECPCB_LITERAL_CBOR" }; #endif diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/CMakeLists.txt b/minimal-examples/crypto/minimal-crypto-cose-key/CMakeLists.txt new file mode 100644 index 000000000..6ee78ae99 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-key/CMakeLists.txt @@ -0,0 +1,55 @@ +project(lws-crypto-cose-key C) +cmake_minimum_required(VERSION 2.8.12) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-crypto-cose-key) +set(SRCS main.c) + +set(requirements 1) +require_lws_config(LWS_WITH_COSE 1 requirements) + +if (requirements) + + add_executable(${SAMP} ${SRCS}) + + add_test(NAME crypto-cose-key-1 + COMMAND lws-crypto-cose-key --stdin set1.cks ) + add_test(NAME crypto-cose-key-2 + COMMAND lws-crypto-cose-key --kty EC2 --curve P-256 --kid ctest-256 --stdout ctest-ec-256.key) + add_test(NAME crypto-cose-key-3 + COMMAND lws-crypto-cose-key --kty EC2 --curve P-384 --kid ctest-384 --stdout ctest-ec-384.key) + add_test(NAME crypto-cose-key-4 + COMMAND lws-crypto-cose-key --kty EC2 --curve P-521 --kid ctest-512 --stdout ctest-ec-512.key) + add_test(NAME crypto-cose-key-5 + COMMAND lws-crypto-cose-key --kty SYMMETRIC --bits 256 --stdout ctest-sym-256.key) + add_test(NAME crypto-cose-key-6 + COMMAND lws-crypto-cose-key --kty RSA --bits 2048 --stdout ctest-rsa-2048.key) + add_test(NAME crypto-cose-key-7 + COMMAND lws-crypto-cose-key --stdin ctest-rsa-2048.key) + + set_tests_properties(crypto-cose-key-1 + crypto-cose-key-2 + crypto-cose-key-3 + crypto-cose-key-4 + crypto-cose-key-5 + crypto-cose-key-6 + crypto-cose-key-7 + PROPERTIES + WORKING_DIRECTORY + ${CMAKE_SOURCE_DIR}/minimal-examples/crypto/minimal-crypto-cose-key + TIMEOUT 5) + + set_tests_properties(crypto-cose-key-7 + PROPERTIES + DEPENDS crypto-cose-key-6) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() +endif() diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/README.md b/minimal-examples/crypto/minimal-crypto-cose-key/README.md new file mode 100644 index 000000000..f6be13db4 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-key/README.md @@ -0,0 +1,132 @@ +# lws minimal example for cose_key + +Demonstrates how to create and dump cose_keys. + +## Dump key or key_set + +Pipe a cose_key or cose_key_set into stdin to get a textual dump of all the keys +inside. You can optionally use --kid kid or --kid-hex HEXSTRING to dump one key +from a set. + +``` +$ cat set1.cks | ./bin/lws-crypto-cose-key +$ cat set1.cks | ./bin/lws-crypto-cose-key --kid 11 +``` + +## Create keys + +Stdin is not used, give parameters for the kty and kid etc to create a +new key on stdout (which can be redirected to a file). + +``` +$ ./bin/lws-crypto-cose-key --kty EC2 --curve P-521 --kid sec512 >ec512.key +``` + +## build + +``` + $ cmake . && make +``` + +## usage + +|Option|Meaning| +|---|---| +|--kty type|Key type, one of OKP, EC2, RSA or SYMMETRIC| +|-k \|One or a set of cose_keys| +|--kid string|Specifies the key ID to use as a string| +|--kid-hex HEXSTRING|Specifies the key ID to use as a hex blob| +|--curve curve|For EC type key creation, specify the curve| +|--stdin filepath|Makes tool fetch from filepath instead of stdin (useful for CI)| +|--stdout filepath|Makes tool write to filepath instead of stdout (useful for CI)| + + +HEXSTRING above means a string like `1a2b3c` + +## Examples + +### cose_key dumping + +``` +$ cat set1.cks | ./bin/lws-crypto-cose-key +[2021/07/30 10:14:31:0420] U: LWS cose-key example tool -k keyset [-s alg-name kid ] +[2021/07/30 10:14:31:0780] N: lws_create_context: LWS: 4.2.99-v4.2.0-134-g8433c8b459, NET CLI SRV H1 H2 WS ConMon IPV6-on +[2021/07/30 10:14:31:0892] N: ++ [wsi|0|pipe] (1) +[2021/07/30 10:14:31:0926] N: ++ [vh|0|netlink] (1) +[2021/07/30 10:14:31:0977] N: ++ [vh|1|default||-1] (2) +[2021/07/30 10:14:31:1057] N: main: importing +Cose key #1 + kty: EC2 + kid: 11 + kty: P-256 + x: bac5b11cad8f99f9c72b05cf4b9e26d244dc189f745228255a219a86d6a09eff + d: 57c92077664146e876760c9520d054aa93c3afb04e306705db6090308507b4d3 + y: 20138bf82dc1b6d562be0fa54ab7804a3a64b6d72ccfed6b6fb6ed28bbfc117e +Cose key #2 + kty: EC2 + kid: meriadoc.brandybuck@buckland.example + kty: P-256 + x: 65eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d + d: aff907c99f9ad3aae6c4cdf21122bce2bd68b5283e6907154ad911840fa208cf + y: 1e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c +Cose key #3 + kty: SYMMETRIC + kid: our-secret + k: 849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188 +Cose key #4 + kty: EC2 + kid: bilbo.baggins@hobbiton.example + kty: P-521 + x: 0072992cb3ac08ecf3e5c63dedec0d51a8c1f79ef2f82f94f3c737bf5de7986671eac625fe8257bbd0394644caaa3aaf8f27a4585fbbcad0f2457620085e5c8f42ad + d: 00085138ddabf5ca975f5860f91a08e91d6d5f9a76ad4018766a476680b55cd339e8ab6c72b5facdb2a2a50ac25bd086647dd3e2e6e99e84ca2c3609fdf177feb26d + y: 01dca6947bce88bc5790485ac97427342bc35f887d86d65a089377e247e60baa55e4e8501e2ada5724ac51d6909008033ebc10ac999b9d7f5cc2519f3fe1ea1d9475 +Cose key #5 + kty: SYMMETRIC + kid: our-secret2 + k: 849b5786457c1491be3a76dcea6c4271 +Cose key #6 + kty: EC2 + kid: peregrin.took@tuckborough.example + kty: P-256 + x: 98f50a4ff6c05861c8860d13a638ea56c3f5ad7590bbfbf054e1c7b4d91d6280 + d: 02d1f7e6f26c43d4868d87ceb2353161740aacf1f7163647984b522a848df1c3 + y: f01400b089867804b8e9fc96c3932161f1934f4223069170d924b7e03bf822bb +Cose key #7 + kty: SYMMETRIC + kid: 018c0ae5-4d9b-471b-bfd6-eef314bc7037 + use: 849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188 +Cose key #8 + kty: SYMMETRIC + kid: sec-48 + k: 849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c42718800112233778899aa2122232425262728 +Cose key #9 + kty: SYMMETRIC + kid: sec-64 + k: 849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c42718800112233778899aa2122232425262728aabbccddeeffa5a6a7a8a9a0b1b2b3b4 +Cose key #10 + kty: EC2 + kid: sec384 + kty: P-384 + x: ea2866349fe3a2f9ad4d6bfe7c30c527436e901c5fb22210b67b2150574ffcd0b1dd8c43d5d1e3d5cb849ecec202117c + d: 4d46a58480d43d5454307edcf501e098ef7c0186cc6b56b41dfd13fe4b9b1ab1425851cf5b23e6636ed18f5bbdde1896 + y: 4c3d245515a688ef25ff68034089ca4f10a01bef51cc57309f12919c3d484142368795c6f2a5d30af650b4e12d0133e4 +Cose key #11 + kty: EC2 + kid: sec512 + kty: P-521 + x: 003b81ed66d8a2194b42f29ecb2c9ae48199be695924804a8407194ed0e172f39693f870f32463e2d36950034a21901487c5a0c43a1713a818fb89fa8a5b3b2dc181 + d: 013e0f06ce394ac14a3df3953fc560679ad0dee14779ef0d475787451fca71e3b4b827b6f7cedcf00e23c716fb829b5419234ba5c92c33e0bc94351fe97be21f2b82 + y: 004b9b6b0adf41913b5d700cf43bfe0ee8b79eb58fc308509e574fcb910b3fd5a2ad585affc6776f7fc9d4ff48f5923fe900660ecc6e3720f89c1363eecfffb38b5b +[2021/07/30 10:14:31:1430] N: -- [wsi|0|pipe] (0) 52.763ms +[2021/07/30 10:14:31:1441] N: -- [vh|0|netlink] (1) 51.437ms +[2021/07/30 10:14:31:1491] N: -- [vh|1|default||-1] (0) 51.591ms +[2021/07/30 10:14:31:1536] N: main: PASS + +``` + +### cose_key creation + +``` +$ ./bin/lws-crypto-cose-key --kty EC2 --curve P-521 --kid sec512 >ec512.key +``` + diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/main.c b/minimal-examples/crypto/minimal-crypto-cose-key/main.c new file mode 100644 index 000000000..5a19e2c88 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-key/main.c @@ -0,0 +1,313 @@ +/* + * lws-minimal-crypto-cose-key + * + * Written in 2010-2021 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include +#include +#include +#include +#include + +static int fdin = 0, fdout = 1; + +static const char *meta_names[] = { + "kty", "kid", "use", "key_ops", "base_iv", "alg" +}; + +static const char *oct_names[] = { + "k" +}; + +static const char *rsa_names[] = { + "e", "n", "d", "p", "q", "dp", "dq", "qi", "other", "ri", "di", "ti" +}; + +static const char *ec_names[] = { + "crv", "x", "d", "y", +}; + +static void +cose_key_dump(const struct lws_cose_key *ck) +{ + const char **enames; + char hex[2048], dump[3072]; + int elems; + size_t l; + int n; + + (void)enames; + (void)meta_names; + + switch (ck->gencrypto_kty) { + + case LWS_GENCRYPTO_KTY_OCT: + elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT; + enames = oct_names; + break; + case LWS_GENCRYPTO_KTY_RSA: + elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT; + enames = rsa_names; + break; + case LWS_GENCRYPTO_KTY_EC: + elems = LWS_GENCRYPTO_EC_KEYEL_COUNT; + enames = ec_names; + break; + + default: + lwsl_err("%s: jwk %p: unknown type\n", __func__, ck); + + return; + } + + for (n = 0; n < LWS_COUNT_COSE_KEY_ELEMENTS; n++) { + if (ck->meta[n].buf) { + if (n < 2) { + l = (size_t)lws_snprintf(dump, sizeof(dump), + " %s: %.*s\n", meta_names[n], + (int)ck->meta[n].len, + ck->meta[n].buf); + write(fdout, dump, l); + } else { + l = (size_t)lws_snprintf(dump, sizeof(dump), + " %s: ", meta_names[n]); + write(fdout, dump, l); + lws_hex_from_byte_array(ck->meta[n].buf, + ck->meta[n].len, + hex, sizeof(hex)); + write(fdout, hex, strlen(hex)); + write(fdout, "\n", 1); + } + } + } + + for (n = 0; n < elems; n++) { + if (ck->e[n].buf) { + if (!n && ck->gencrypto_kty == LWS_GENCRYPTO_KTY_EC) { + l = (size_t)lws_snprintf(dump, sizeof(dump), + " %s: %.*s\n", enames[n], + (int)ck->e[n].len, + ck->e[n].buf); + write(fdout, dump, l); + } else { + l = (size_t)lws_snprintf(dump, sizeof(dump), + " %s: ", enames[n]); + write(fdout, dump, l); + lws_hex_from_byte_array(ck->e[n].buf, + ck->e[n].len, + hex, sizeof(hex)); + write(fdout, hex, strlen(hex)); + write(fdout, "\n", 1); + } + } + } +} + +int main(int argc, const char **argv) +{ + uint8_t *kid = NULL, ktmp[4096], set_temp[32 * 1024], temp[256]; + int result = 1, bits = 0, + logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + struct lws_context_creation_info info; + size_t kid_len = 0, stp = 0; + struct lws_context *context; + lws_cose_key_t *ck = NULL; + cose_param_t cose_kty = 0; + lws_dll2_owner_t set; + const char *p, *crv; + lws_lec_pctx_t lec; + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + + lwsl_user("LWS cose-key example tool -k keyset [-s alg-name kid ]\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ +#if defined(LWS_WITH_NETWORK) + info.port = CONTEXT_PORT_NO_LISTEN; +#endif + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + if ((p = lws_cmdline_option(argc, argv, "--stdin"))) { + fdin = open(p, LWS_O_RDONLY, 0); + if (fdin < 0) { + lwsl_err("%s: unable to open stdin file\n", __func__); + return 1; + } + } + + if ((p = lws_cmdline_option(argc, argv, "--stdout"))) { + fdout = open(p, LWS_O_WRONLY | LWS_O_CREAT | LWS_O_TRUNC, 0600); + if (fdout < 0) { + lwsl_err("%s: unable to open stdout file\n", __func__); + goto bail_early; + } + } + + if ((p = lws_cmdline_option(argc, argv, "--kid"))) { + kid = (uint8_t *)p; + kid_len = strlen(p); + //lwsl_hexdump_notice(kid, kid_len); + } + + if ((p = lws_cmdline_option(argc, argv, "--kid-hex"))) { + kid_len = (size_t)lws_hex_to_byte_array(p, ktmp, sizeof(ktmp)); + kid = (uint8_t *)ktmp; + } + + /* + * If we have some stdin queued up, we understand we are dumping + * an existing cose_key or key_set from stdin + */ + + if (!fdin) { + struct timeval timeout; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(0, &fds); + + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + + if (select(fdin + 1, &fds, NULL, NULL, &timeout) < 0) + goto no_stdin; + + if (!FD_ISSET(0, &fds)) + goto no_stdin; + } + + do { + int n = (int)read(fdin, temp, sizeof(temp)); + + if (n < 0) + goto bail; + if (!n) { + int kc = 0; + + if (!stp) + /* there was no stdin */ + break; + + lwsl_notice("%s: importing\n", __func__); + + lws_dll2_owner_clear(&set); + ck = lws_cose_key_import(&set, NULL, NULL, set_temp, stp); + if (!ck) { + lwsl_err("%s: import failed\n", __func__); + goto bail; + } + + lws_start_foreach_dll(struct lws_dll2 *, p, + lws_dll2_get_head(&set)) { + lws_cose_key_t *ck = lws_container_of(p, + lws_cose_key_t, list); + struct lws_gencrypto_keyelem *ke = + &ck->meta[COSEKEY_META_KID]; + + kc++; + + if (!kid_len || (ke->len && + ke->len == (uint32_t)kid_len && + !memcmp(ke->buf, kid, kid_len))) { + printf("Cose key #%d\n", kc); + cose_key_dump(ck); + } + + } lws_end_foreach_dll(p); + + lws_cose_key_set_destroy(&set); + result = 0; + goto bail; + + } + + if (stp + (size_t)n > sizeof(set_temp)) { + lwsl_err("%s: stdin bigger than our buffer\n", __func__); + goto bail; + } + memcpy(set_temp + stp, temp, (size_t)n); + stp += (size_t)n; + } while (1); + +no_stdin: + + /* + * + */ + + p = lws_cmdline_option(argc, argv, "--kty"); + if (!p) { + lwsl_err("%s: use --kty OKP|EC2|RSA|SYMMETRIC\n", + __func__); + goto bail; + } + + if (!strcmp(p, "OKP")) + cose_kty = LWSCOSE_WKKTV_OKP; + if (!strcmp(p, "EC2")) + cose_kty = LWSCOSE_WKKTV_EC2; + if (!strcmp(p, "RSA")) + cose_kty = LWSCOSE_WKKTV_RSA; + if (!strcmp(p, "SYMMETRIC") || !strcmp(p, "SYM")) + cose_kty = LWSCOSE_WKKTV_SYMMETRIC; + + if (!cose_kty) { + lwsl_err("%s: use --kty OKP|EC2|RSA|SYMMETRIC\n", + __func__); + goto bail; + } + + crv = NULL; + if (cose_kty == LWSCOSE_WKKTV_OKP || + cose_kty == LWSCOSE_WKKTV_EC2) { + crv = lws_cmdline_option(argc, argv, "--curve"); + if (!crv) { + lwsl_err("%s: use --curve P-256 etc\n", __func__); + goto bail; + } + } + + p = lws_cmdline_option(argc, argv, "--bits"); + if (p) + bits = atoi(p); + + ck = lws_cose_key_generate(context, cose_kty, 0, bits, crv, + kid, kid_len); + if (!ck) + goto bail; + + lws_lec_init(&lec, ktmp, sizeof(ktmp)); + lws_cose_key_export(ck, &lec, LWSJWKF_EXPORT_PRIVATE); + write(fdout, ktmp, lec.used); + + lws_cose_key_destroy(&ck); + result = 0; + +bail: + lws_context_destroy(context); + + if (result) + lwsl_err("%s: FAIL: %d\n", __func__, result); + else + lwsl_notice("%s: PASS\n", __func__); + +bail_early: + if (fdin > 0) + close(fdin); + if (fdout != 1 && fdout >= 0) + close(fdout); + + return result; +} diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/set1.cks b/minimal-examples/crypto/minimal-crypto-cose-key/set1.cks new file mode 100644 index 0000000000000000000000000000000000000000..e5eca18e357687a9cd802248587a35c2565119cc GIT binary patch literal 1342 zcmeBP#>m9vWN4_ss2HKJ>*z+Awf!@H9@l0)?>$fLlFJ>5`6WRbs!@uw+O92__g^VO zK|#3thwi~`SCjVfFZJ5q;ANGv?YhqSx7qpI-fHarBUq;#p%8vjp*+pe?L}D`&s2pA zA*&`IUcbT5Af5Gg!UTg>_AQq|Hb$uArWR!;rsOBP)bcSpxQ@wL! zy>1G&@Gs&xznGDQ$t%CKNVhmOxhSLcDUl)bsqOpa@lWhH4e+0p4R4CBQkNH zRoR_aIZlNgU~kAJW#%O1>m?=?mlWUm* zM_f$5(^{nnC&oL=rc|Hn*b_d%BkE*{x{3DT_>S7PYf&7N%OANv<6af|QBL26NIdQoPcUP*p_wnGUpUX${R@=Mb*;2{G_Z!^Af`F}eQk$9qw zS9qDltFXgg*OpG${rf}6!{b|S$|f}cqxORc!-meb3YHx&|4chPSuyeBWPc}RwuuEd zRklB{{-LxR7`03nzd!qw<9wy9xBc8EQ^Uj(t~DROiy>HT;Z5rDm!Xq5#EflK`X~^8y2>t78^3Y>(n|NDbo9FqSBM+XZ3a@`f-Ua( X@v2^*cbh$5beXYjy52thomsX39o|<- literal 0 HcmV?d00001 diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass01.sig b/minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass01.sig new file mode 100644 index 0000000000000000000000000000000000000000..d78767e6115b91474ea477519452b87e08736974 GIT binary patch literal 98 zcmccA;<#WDqZ*5op%r \ No newline at end of file diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass03.sig b/minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass03.sig new file mode 100644 index 000000000..a10fc966e --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-key/sign1_pass03.sig @@ -0,0 +1 @@ +C&B11TThis is the content.X@>LFZZ4k#Տ\1Z~*2J4V*"4DT~; E6 \ No newline at end of file diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass01.sig b/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass01.sig new file mode 100644 index 000000000..268b08618 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass01.sig @@ -0,0 +1 @@ +bATThis is the content.C&B11X@⮯ iѝnR|](,].قETLEHj+:/ diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass02.sig b/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass02.sig new file mode 100644 index 000000000..b80f7a26f --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass02.sig @@ -0,0 +1 @@ +b@TThis is the content.C&B11X@˸پMkOZ$2>tQ?&rٓDvG1W̨k diff --git a/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass03.sig b/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass03.sig new file mode 100644 index 000000000..60d8f0394 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-key/sign_pass03.sig @@ -0,0 +1 @@ +@TThis is the content.C&B11X@⮯ iѝnR|](,].قETLEHj+:/ diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/CMakeLists.txt b/minimal-examples/crypto/minimal-crypto-cose-sign/CMakeLists.txt new file mode 100644 index 000000000..b49b4173e --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/CMakeLists.txt @@ -0,0 +1,219 @@ +project(lws-crypto-cose-sign C) +cmake_minimum_required(VERSION 2.8.12) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-crypto-cose-sign) +set(SRCS main.c) + +set(requirements 1) +require_lws_config(LWS_WITH_COSE 1 requirements) + +if (requirements) + + add_executable(${SAMP} ${SRCS}) + + # EC signing + + add_test(NAME crypto-cose-sign-1 + COMMAND lws-crypto-cose-sign -s -k set1.cks --kid 11 + --alg ES256 --cose-sign + --stdin payload.txt + --stdout ctest-sig-es256.sig) + add_test(NAME crypto-cose-sign-2 + COMMAND lws-crypto-cose-sign -s -k set1.cks --kid sec384 + --alg ES384 --cose-sign + --stdin payload.txt + --stdout ctest-sig-es384.sig) + add_test(NAME crypto-cose-sign-3 + COMMAND lws-crypto-cose-sign -s -k set1.cks --kid sec512 + --alg ES512 --cose-sign + --stdin payload.txt + --stdout ctest-sig-es512.sig) + + # EC validation + + add_test(NAME crypto-cose-sign-4 + COMMAND lws-crypto-cose-sign -k set1.cks + --stdout r1.txt + --stdin ctest-sig-es256.sig) + set_tests_properties(crypto-cose-sign-4 PROPERTIES + DEPENDS crypto-cose-sign-1) + + add_test(NAME crypto-cose-sign-5 + COMMAND lws-crypto-cose-sign -k set1.cks + --stdout r2.txt + --stdin ctest-sig-es384.sig) + set_tests_properties(crypto-cose-sign-5 PROPERTIES + DEPENDS crypto-cose-sign-2) + + add_test(NAME crypto-cose-sign-6 + COMMAND lws-crypto-cose-sign -k set1.cks --cose-sign + --stdout r3.txt + --stdin ctest-sig-es512.sig) + set_tests_properties(crypto-cose-sign-6 PROPERTIES + DEPENDS crypto-cose-sign-3) + + # RSA 4096 signing + + add_test(NAME crypto-cose-sign-7 + COMMAND lws-crypto-cose-sign -s -k rsa-4096.ck + --alg RS512 --cose-sign + --stdin payload.txt + --stdout ctest-sig-rs512.sig) + + # RSA 4096 validation + + add_test(NAME crypto-cose-sign-8 + COMMAND lws-crypto-cose-sign -k rsa-4096.ck --cose-sign + --stdout r8.txt + --stdin ctest-sig-rs512.sig) + set_tests_properties(crypto-cose-sign-8 PROPERTIES + DEPENDS crypto-cose-sign-7) + + # HMAC signing, cose-mac + +# add_test(NAME crypto-cose-sign-9 +# COMMAND lws-crypto-cose-sign -s -k set1.cks --kid our-secret +# --alg HS256 --cose-mac +# --stdin payload.txt +# --stdout ctest-sig-hmac256.sig) +# add_test(NAME crypto-cose-sign-10 +# COMMAND lws-crypto-cose-sign -s -k set1.cks --kid sec-48 +# --alg HS384 --cose-mac +# --stdin payload.txt +# --stdout ctest-sig-hmac384.sig) +# add_test(NAME crypto-cose-sign-11 +# COMMAND lws-crypto-cose-sign -s -k set1.cks --kid sec-64 +# --alg HS512 --cose-mac +# --stdin payload.txt +# --stdout ctest-sig-hmac512.sig) +# add_test(NAME crypto-cose-sign-12 +# COMMAND lws-crypto-cose-sign -s -k set1.cks --kid our-secret +# --alg HS256_64 --cose-mac +# --stdin payload.txt +# --stdout ctest-sig-hmac256_64.sig) + + # HMAC validation, cose-mac + +# add_test(NAME crypto-cose-sign-13 +# COMMAND lws-crypto-cose-sign -k set1.cks +# --stdout r1.txt +# --stdin ctest-sig-hmac256.sig) +# set_tests_properties(crypto-cose-sign-13 +# PROPERTIES DEPENDS crypto-cose-sign-9) +# +# add_test(NAME crypto-cose-sign-14 +# COMMAND lws-crypto-cose-sign -k set1.cks +# --stdout r2.txt +# --stdin ctest-sig-hmac384.sig) +# set_tests_properties(crypto-cose-sign-14 +# PROPERTIES DEPENDS crypto-cose-sign-10) +# +# add_test(NAME crypto-cose-sign-15 +# COMMAND lws-crypto-cose-sign -k set1.cks +# --stdout r3.txt +# --stdin ctest-sig-hmac512.sig) +# set_tests_properties(crypto-cose-sign-15 +# PROPERTIES DEPENDS crypto-cose-sign-11) +# +# add_test(NAME crypto-cose-sign-16 +# COMMAND lws-crypto-cose-sign -k set1.cks +# --stdout r4.txt +# --stdin ctest-sig-hmac256_64.sig) +# set_tests_properties(crypto-cose-sign-16 +# PROPERTIES DEPENDS crypto-cose-sign-12) + + # HMAC signing, cose-mac0 + + add_test(NAME crypto-cose-sign-17 + COMMAND lws-crypto-cose-sign -s -k set1.cks --kid our-secret + --alg HS256 --cose-mac0 + --stdin payload.txt + --stdout ctest-sig-hmac0256.sig) + add_test(NAME crypto-cose-sign-18 + COMMAND lws-crypto-cose-sign -s -k set1.cks --kid sec-48 + --alg HS384 --cose-mac0 + --stdin payload.txt + --stdout ctest-sig-hmac0384.sig) + add_test(NAME crypto-cose-sign-19 + COMMAND lws-crypto-cose-sign -s -k set1.cks --kid sec-64 + --alg HS512 --cose-mac0 + --stdin payload.txt + --stdout ctest-sig-hmac0512.sig) + add_test(NAME crypto-cose-sign-20 + COMMAND lws-crypto-cose-sign -s -k set1.cks --kid our-secret + --alg HS256_64 --cose-mac0 + --stdin payload.txt + --stdout ctest-sig-hmac0256_64.sig) + + # HMAC validation, cose-mac0 + + add_test(NAME crypto-cose-sign-21 + COMMAND lws-crypto-cose-sign -k set1.cks + --stdout r1.txt + --stdin ctest-sig-hmac0256.sig) + set_tests_properties(crypto-cose-sign-21 + PROPERTIES DEPENDS crypto-cose-sign-17) + + add_test(NAME crypto-cose-sign-22 + COMMAND lws-crypto-cose-sign -k set1.cks + --stdout r2.txt + --stdin ctest-sig-hmac0384.sig) + set_tests_properties(crypto-cose-sign-22 + PROPERTIES DEPENDS crypto-cose-sign-18) + + add_test(NAME crypto-cose-sign-23 + COMMAND lws-crypto-cose-sign -k set1.cks + --stdout r3.txt + --stdin ctest-sig-hmac0512.sig) + set_tests_properties(crypto-cose-sign-23 + PROPERTIES DEPENDS crypto-cose-sign-19) + + add_test(NAME crypto-cose-sign-24 + COMMAND lws-crypto-cose-sign -k set1.cks + --stdout r4.txt + --stdin ctest-sig-hmac0256_64.sig) + set_tests_properties(crypto-cose-sign-24 + PROPERTIES DEPENDS crypto-cose-sign-20) + + + set_tests_properties(crypto-cose-sign-1 + crypto-cose-sign-2 + crypto-cose-sign-3 + crypto-cose-sign-4 + crypto-cose-sign-5 + crypto-cose-sign-6 + crypto-cose-sign-7 + crypto-cose-sign-8 +# crypto-cose-sign-9 +# crypto-cose-sign-10 +# crypto-cose-sign-11 +# crypto-cose-sign-12 +# crypto-cose-sign-13 +# crypto-cose-sign-14 +# crypto-cose-sign-15 +# crypto-cose-sign-16 + crypto-cose-sign-17 + crypto-cose-sign-18 + crypto-cose-sign-19 + crypto-cose-sign-20 + crypto-cose-sign-21 + crypto-cose-sign-22 + crypto-cose-sign-23 + crypto-cose-sign-24 + + PROPERTIES + WORKING_DIRECTORY + ${CMAKE_SOURCE_DIR}/minimal-examples/crypto/minimal-crypto-cose-sign + TIMEOUT 5) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) + endif() +endif() diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/README.md b/minimal-examples/crypto/minimal-crypto-cose-sign/README.md new file mode 100644 index 000000000..f241d75ea --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/README.md @@ -0,0 +1,105 @@ +# lws minimal example for cose_sign + +Demonstrates how to sign and verify using cose_sign and cose_key, providing a +commandline tool for signing and verifying stdin. + +## build + +``` + $ cmake . && make +``` + +## usage + +|Option|Sig|Val|Meaning| +|---|---|---|---| +|-s|o|||Select signing mode (stdin is payload)| +|-k |o|o|One or a set of cose_keys| +|--kid string|o|mac0|Specifies the key ID to use as a string| +|--kid-hex HEXSTRING|o|mac0|Specifies the key ID to use as a hex blob| +|--cose-sign|o|if no tag|Sets cose-sign mode| +|--cose-sign1|o|if no tag|Sets cose-sign1 mode| +|--cose-mac|o|if no tag|Sets cose-sign1 mode| +|--cose-mac0|o|if no tag|Sets cose-sign1 mode| +|--extra HEXSTRING|o|o|Optional extra payload data| + +HEXSTRING above means a string like `1a2b3c` + +Stdin is either the plaintext (if signing) or cose_sign (if verifying). + +For convenience, a keyset from the COSE RFC is provided in +`minimal-examples/crypto/minimal-crypto-cose-sign/set1.cks`. Six example +cose_sign1 and cose_sign are also provided in that directory signed with keys +from the provided keyset. + +## Examples + +### Validation + +The RFC8152 sign1_pass01.sig is a cose_sign1 that contains the ES256 alg +parameter along with a kid hint that it was signed with the key with kid "11" +from the RFC8152 key set. So we just need to provide the signature and the key +set and lws can sort it out. + +``` +$ cat sign1_pass01.sig | ./lws-crypto-cose-sign -k set1.cks +[2021/07/26 05:41:29:1663] N: lws_create_context: LWS: 4.2.99-v4.2.0-133-g300f3f3250, NET CLI SRV H1 H2 WS ConMon IPV6-on +[2021/07/26 05:41:29:3892] N: results count 1 +[2021/07/26 05:41:29:3901] N: result: 0 (alg ES256, kid 3131) +[2021/07/26 05:41:29:4168] N: main: PASS +``` + +Notice how the validation just delivers a results list and leaves it to the user +code to iterate it, and confirm that it's happy with the result, the alg used, +and the kid that was used. + +RFC8152 sign1_pass02.sig is similar but contains extra application data in the +signature, that must be given at validation too. + +``` +$cat sign1_pass02.sig | ./lws-crypto-cose-sign -k set1.cks --extra 11aa22bb33cc44dd55006699 +[2021/07/26 05:55:50:9103] N: lws_create_context: LWS: 4.2.99-v4.2.0-133-g300f3f3250, NET CLI SRV H1 H2 WS ConMon IPV6-on +[2021/07/26 05:55:50:9381] N: 12 +[2021/07/26 05:55:51:0924] N: +[2021/07/26 05:55:51:0939] N: 0000: 11 AA 22 BB 33 CC 44 DD 55 00 66 99 ..".3.D.U.f. +[2021/07/26 05:55:51:0943] N: +[2021/07/26 05:55:51:1368] N: results count 1 +[2021/07/26 05:55:51:1377] N: result: 0 (alg ES256, kid 3131) +[2021/07/26 05:55:51:1657] N: main: PASS +``` + +### Signing + +Generate a cose-sign1 using ES256 and the key set key with id "11" for the +payload given on stdin + +``` +$ echo -n "This is the content." |\ + ./bin/lws-crypto-cose-sign -s -k set1.cks \ + --kid 11 --alg ES256 > ./test.sig + +00000000 d2 84 43 a1 01 26 a1 04 42 31 31 54 54 68 69 73 |..C..&..B11TThis| +00000010 20 69 73 20 74 68 65 20 63 6f 6e 74 65 6e 74 2e | is the content.| +00000020 58 40 b9 a8 85 09 17 7f 01 f6 78 5d 39 62 d0 44 |X@........x]9b.D| +00000030 08 0b fa b4 b4 5b 17 80 c2 e3 ba a3 af 33 6f e6 |.....[.......3o.| +00000040 44 09 13 1f cf 4f 17 5c 62 9f 8d 29 29 1c ab 28 |D....O.\b..))..(| +00000050 b2 f4 e6 af f9 62 ea 69 52 90 07 0e 2c 40 72 d3 |.....b.iR...,@r.| +00000060 12 cf |..| + +``` + +Same as above, but force it to use cose-sign layout + +``` +$ echo -n "This is the content." |\ + ./bin/lws-crypto-cose-sign -s -k set1.cks \ + --kid 11 --alg ES256 --cose-sign > ./test.sig + +00000000 d8 62 84 40 40 54 54 68 69 73 20 69 73 20 74 68 |.b.@@TThis is th| +00000010 65 20 63 6f 6e 74 65 6e 74 2e 81 83 a1 01 26 a1 |e content.....&.| +00000020 04 42 31 31 58 40 37 5d 93 48 20 b0 d0 75 16 41 |.B11X@7].H ..u.A| +00000030 db 95 95 5b 39 7d 6d 92 6e 52 c9 78 96 d8 a2 9b |...[9}m.nR.x....| +00000040 62 62 89 9e e5 26 31 63 4b 90 d1 37 86 ca 82 a2 |bb...&1cK..7....| +00000050 28 9a d2 82 a7 6d 24 23 cd de 58 91 47 98 bb 11 |(....m$#..X.G...| +00000060 e4 b9 08 18 48 65 |....He| +``` diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/main.c b/minimal-examples/crypto/minimal-crypto-cose-sign/main.c new file mode 100644 index 000000000..c1e8e5889 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/main.c @@ -0,0 +1,406 @@ +/* + * lws-minimal-crypto-cose-sign + * + * Written in 2010-2021 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include +#include +#include + +static int fdin = 0, fdout = 1; +static uint8_t extra[4096]; +static size_t ext_len; + +int +_alloc_file(struct lws_context *context, const char *filename, uint8_t **buf, + size_t *amount) +{ + FILE *f; + size_t s; + ssize_t m; + int n = 0; + + f = fopen(filename, "rb"); + if (f == NULL) { + n = 1; + goto bail; + } + + if (fseek(f, 0, SEEK_END) != 0) { + n = 1; + goto bail; + } + + m = ftell(f); + if (m == -1l) { + n = 1; + goto bail; + } + s = (size_t)m; + + if (fseek(f, 0, SEEK_SET) != 0) { + n = 1; + goto bail; + } + + *buf = malloc(s + 1); + if (!*buf) { + n = 2; + goto bail; + } + + if (fread(*buf, s, 1, f) != 1) { + free(*buf); + n = 1; + goto bail; + } + + *amount = s; + +bail: + if (f) + fclose(f); + + return n; + +} + +static int +extra_cb(lws_cose_sig_ext_pay_t *x) +{ + x->ext = extra; + x->xl = ext_len; + + // lwsl_hexdump_notice(extra, ext_len); + + return 0; +} + +int +pay_cb(struct lws_cose_validate_context *cps, void *opaque, + const uint8_t *paychunk, size_t paychunk_len) +{ + write(fdout, paychunk, paychunk_len); + + return 0; +} + +int main(int argc, const char **argv) +{ + uint8_t *ks, temp[256], *kid = NULL, ktmp[4096], sbuf[512]; + int n, m, sign = 0, result = 1, + logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + enum lws_cose_sig_types sigtype = SIGTYPE_UNKNOWN; + struct lws_cose_validate_context *cps = NULL; + struct lws_cose_sign_context *csc = NULL; + const struct lws_gencrypto_keyelem *ke; + struct lws_context_creation_info info; + lws_cose_validate_create_info_t vi; + struct lws_buflist *paybuf = NULL; + lws_cose_sign_create_info_t i; + struct lws_context *context; + size_t ks_len, kid_len = 0; + lws_cose_key_t *ck = NULL; + lws_dll2_owner_t *o, set; + lws_lec_pctx_t lec; + cose_param_t alg; + const char *p; + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + + lwsl_user("LWS cose-sign example tool -k keyset [-s alg-name kid ]\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ +#if defined(LWS_WITH_NETWORK) + info.port = CONTEXT_PORT_NO_LISTEN; +#endif + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + if ((p = lws_cmdline_option(argc, argv, "--stdin"))) { + fdin = open(p, LWS_O_RDONLY, 0); + if (fdin < 0) { + lwsl_err("%s: unable to open stdin file\n", __func__); + return 1; + } + } + + if ((p = lws_cmdline_option(argc, argv, "--stdout"))) { + fdout = open(p, LWS_O_WRONLY | LWS_O_CREAT | LWS_O_TRUNC, 0600); + if (fdout < 0) { + lwsl_err("%s: unable to open stdout file\n", __func__); + goto bail_early; + } + } + + /* + * If no tag, you can tell it the signature type, otherwise it will + * use the tag to select the right type without these + */ + + if (lws_cmdline_option(argc, argv, "--cose-sign")) + sigtype = SIGTYPE_MULTI; + + if (lws_cmdline_option(argc, argv, "--cose-sign1")) + sigtype = SIGTYPE_SINGLE; + + if (lws_cmdline_option(argc, argv, "--cose-mac")) + sigtype = SIGTYPE_MAC; + + if (lws_cmdline_option(argc, argv, "--cose-mac0")) + sigtype = SIGTYPE_MAC0; + + /* if signing, set the ciphers */ + + if (lws_cmdline_option(argc, argv, "-s")) + sign = 1; + + if ((p = lws_cmdline_option(argc, argv, "--kid"))) { + kid = (uint8_t *)p; + kid_len = strlen(p); + //lwsl_hexdump_notice(kid, kid_len); + } + + if ((p = lws_cmdline_option(argc, argv, "--kid-hex"))) { + kid_len = (size_t)lws_hex_to_byte_array(p, ktmp, sizeof(ktmp)); + kid = (uint8_t *)ktmp; + } + + if ((p = lws_cmdline_option(argc, argv, "--extra"))) { + ext_len = (size_t)lws_hex_to_byte_array(p, extra, sizeof(extra)); + lwsl_notice("%llu\n", (unsigned long long)ext_len); + if (ext_len == (size_t)-1ll) + ext_len = 0; + } + + /* grab the key */ + + if (!(p = lws_cmdline_option(argc, argv, "-k"))) { + lwsl_err("-k is required\n"); + goto bail; + } + + if (_alloc_file(context, p, &ks, &ks_len)) { + lwsl_err("%s: unable to load %s\n", __func__, p); + goto bail; + } + + lws_dll2_owner_clear(&set); + if (!lws_cose_key_import(&set, NULL, NULL, ks, ks_len)) { + lwsl_notice("%s: key import fail\n", __func__); + free(ks); + goto bail2; + } + + free(ks); + + if (!fdin) { + struct timeval timeout; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(0, &fds); + + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + + if (select(fdin + 1, &fds, NULL, NULL, &timeout) < 0 || + !FD_ISSET(0, &fds)) { + lwsl_err("%s: pass cose_sign or plaintext " + "on stdin or --stdin\n", __func__); + goto bail2; + } + } + + if (sign) { + uint8_t *ppay; + size_t s; + + p = lws_cmdline_option(argc, argv, "--alg"); + if (!p) { + lwsl_err("%s: need to specify alg (eg, ES256) " + "when signing\n", __func__); + goto bail2; + } + alg = lws_cose_name_to_alg(p); + + lws_lec_init(&lec, sbuf, sizeof(sbuf)); + memset(&i, 0, sizeof(i)); + i.cx = context; + i.keyset = &set; + i.lec = &lec; + i.flags = LCSC_FL_ADD_CBOR_TAG | + LCSC_FL_ADD_CBOR_PREFER_MAC0; + i.sigtype = sigtype; + + /* + * Unfortunately, with COSE we must know the payload length + * before we have seen the payload. It's illegal to use + * indeterminite lengths inside COSE objects. + */ + + do { + n = (int)read(fdin, temp, sizeof(temp)); + if (n < 0) + goto bail3; + if (!n) + break; + + s = (size_t)n; + + if (lws_buflist_append_segment(&paybuf, temp, s) < 0) + goto bail3; + i.inline_payload_len += s; + + } while (1); + + // lwsl_notice("%s: inline_payload_len %llu\n", __func__, + // (unsigned long long)i.inline_payload_len); + + csc = lws_cose_sign_create(&i); + if (!csc) + goto bail2; + ck = lws_cose_key_from_set(&set, kid, kid_len); + if (!ck) + goto bail2; + + if (lws_cose_sign_add(csc, alg, ck)) + goto bail2; + + do { + s = lws_buflist_next_segment_len(&paybuf, &ppay); + if (!s) + break; + + do { + m = (int)lws_cose_sign_payload_chunk(csc, + ppay, s); + if (lec.used) { + // lwsl_hexdump_err(sbuf, lec.used); + write(fdout, sbuf, lec.used); + lws_lec_setbuf(&lec, sbuf, sizeof(sbuf)); + } + } while (m == LCOSESIGEXTCB_RET_AGAIN); + + if (m == LWS_LECPCTX_RET_FAIL) + goto bail2; + + if (lec.used) { + write(fdout, sbuf, lec.used); + lws_lec_setbuf(&lec, sbuf, sizeof(sbuf)); + } + + lws_buflist_use_segment(&paybuf, s); + } while(1); + + } else { + memset(&vi, 0, sizeof(vi)); + + vi.cx = context; + vi.keyset = &set; + vi.sigtype = sigtype; + vi.ext_cb = extra_cb; + vi.ext_opaque = extra; + vi.ext_len = ext_len; + vi.pay_cb = pay_cb; + + cps = lws_cose_validate_create(&vi); + if (!cps) { + lwsl_notice("%s: sign_val_create fail\n", __func__); + goto bail; + } + + do { + n = (int)read(fdin, temp, sizeof(temp)); + if (n < 0) + goto bail3; + if (!n) + break; + + n = lws_cose_validate_chunk(cps, temp, (size_t)n, NULL); + if (n && n != LECP_CONTINUE) { + lwsl_err("%s: chunk validation failed: %d\n", + __func__, n); + goto bail2; + } + } while (1); + } + +bail3: + + result = 0; + + if (!sign) { + char buf[2048]; + int os; + + o = lws_cose_validate_results(cps); + if (!o) + result = 1; + else { + os = lws_snprintf(buf, sizeof(buf), + "\nresults count %d\n", o->count); + write(fdout, buf, (size_t)os); + + if (!o->count) + result = 1; + } + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(o)) { + lws_cose_validate_res_t *res = lws_container_of(p, + lws_cose_validate_res_t, list); + char khr[256]; + + khr[0] = '\0'; + if (res->cose_key) { + ke = &res->cose_key->meta[COSEKEY_META_KID]; + if (ke && ke->buf) + lws_hex_from_byte_array(ke->buf, ke->len, + khr, sizeof(khr)); + } + os = lws_snprintf(buf, sizeof(buf), + " result: %d (alg %s, kid %s)\n", + res->result, + lws_cose_alg_to_name(res->cose_alg), khr); + write(fdout, buf, (size_t)os); + result |= res->result; + } lws_end_foreach_dll_safe(p, tp); + } + +bail2: + if (!sign) + lws_cose_validate_destroy(&cps); + else { + lws_buflist_destroy_all_segments(&paybuf); + lws_cose_sign_destroy(&csc); + } +//bail1: + lws_cose_key_set_destroy(&set); +bail: + lws_context_destroy(context); + + if (result) + lwsl_err("%s: FAIL: %d\n", __func__, result); + else + lwsl_notice("%s: PASS\n", __func__); + +bail_early: + if (fdin > 0) + close(fdin); + if (fdout != 1 && fdout >= 0) + close(fdout); + + return result; +} diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/payload.txt b/minimal-examples/crypto/minimal-crypto-cose-sign/payload.txt new file mode 100644 index 000000000..402bd7c43 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/payload.txt @@ -0,0 +1 @@ +The Test Payload \ No newline at end of file diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/rsa-4096.ck b/minimal-examples/crypto/minimal-crypto-cose-sign/rsa-4096.ck new file mode 100644 index 0000000000000000000000000000000000000000..8b3fddb9f30ba3028ad5cd519dd248572c38365a GIT binary patch literal 1569 zcmV++2HyFn0RsX@a&uucFgZ3MLjeE*AXx$c_}y}{+}#-0U7Q)MfE$l6kdu^h1&*Z4QLNevHZYaPDnB?1?I!QtQME z`KyF;LAlM$!QmSsTblfaHt^Sk#ao5$*g306zV?z++gaUyb5$ag@D0&jD)d=R6C96i z<317=(OZay?sya3ELlOCY90usaIbD|qu-#FPP76pdgkj(iP#H=swutc;}#nZ&56Ku ziGFT3V;{L>QQ&)isiY+pL9&YW{-JvsA)oH^@^G?NM}{{%C7_1|i6=1M*&280UdQ+Y>kl`z{gkonJq7)s~(B3S|ej=_J3ZZY#S=0X!L$`aDw*NR;3D)?BmN@e?Z z4DsZgR9*<5e+MQY>rt0L5!B9%OCl#GW?yBX44ofX>|Ai&r}b1$f8C-%+$}3A4n_a* zyl5%bB9{U4jaOhRT<@aFoOQ2Nk0KjEbnfWR`rxz4002_afUy4a%=GYR%*TK|N7!J3 z?uHkcYu;+yCV?({7f@--6|Y`r@vW&7sRF-qa-$3ndHkjigc^7{eumf zaXsGuEV{R?>sEx2PHWmkie%&Qi!-N32K5qPVYsi=&W0zVTv9CFMfl+iSI}(5ni0l< z^%NuZ&kk8$02%{YTbV_@g1(oKei-~FLCkCCQvNqLGndcCgN?Gq;g=MPY$-4JGPzcP zvOlBY<-obFl8x=6mm0O}_3D|~Z5oRInWzg^Xhj(FMjcsy^qy|j?su#ZMBL@~3s z)(!WkxjE~E8$nnc@xIB-sk+AiUZYSSt1+EgU(@bM9Q7z zl56T8%~2K4mfcAx>d>WI)R()7VI*zpI1onSw=pVO!I zdTmcngWk{33DR8=7Z@O+mFA!XbwC1{s>yAEXXyT5q=LC(&RD#L0Q#Dj)~p{tTcYJf z)1Xx?y~iMoA{*>Yi=K}wB$aBPwaMN89iA$mK8^?r!0-05Rs9Vi&ou%~1?8wfqhwT6 zVIap0zOaEl1_YJ0T?}YWB#6~j-KL^-ijC!WcB-l+Jh5itqu(_8k$D9oY?7%QCD7|BDHaUhxqhg-acZ)TL zAv(`-TeEwBv`pn(FtcA_Bv}Cf`P=F>?aODt=s*{WEjb`OnC%*HY-$Z~sK?<=P>TBJ z1=?UHi~FXz3hPLwH!wwjpCAgDrs&^yjovD~__)mW$`*AHSE&~t#{3UvQ~5ZVWNU6V z^JZbvt7^Zk^2n8wWDz?3j6rJ#R|vXUp~N~7OVo&lThlitD<~LJe56RU*B15|r-6Xx z==xXA{Y(gzVvgQnmfX|t7Sz>leOcg6E>N9VQQ)sDf^MoVT+XK|3Sk9W`WHJ<33n~| z7&()v4TIoJ`Ptu$UT)sKbv#H{Qpux&|4Xg#cEY>&DMVxbjnHyMvy@E3?(18`!b}fC TWusG*BG=(X%PA@DE1&xR>HHL} literal 0 HcmV?d00001 diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/set1.cks b/minimal-examples/crypto/minimal-crypto-cose-sign/set1.cks new file mode 100644 index 0000000000000000000000000000000000000000..e5eca18e357687a9cd802248587a35c2565119cc GIT binary patch literal 1342 zcmeBP#>m9vWN4_ss2HKJ>*z+Awf!@H9@l0)?>$fLlFJ>5`6WRbs!@uw+O92__g^VO zK|#3thwi~`SCjVfFZJ5q;ANGv?YhqSx7qpI-fHarBUq;#p%8vjp*+pe?L}D`&s2pA zA*&`IUcbT5Af5Gg!UTg>_AQq|Hb$uArWR!;rsOBP)bcSpxQ@wL! zy>1G&@Gs&xznGDQ$t%CKNVhmOxhSLcDUl)bsqOpa@lWhH4e+0p4R4CBQkNH zRoR_aIZlNgU~kAJW#%O1>m?=?mlWUm* zM_f$5(^{nnC&oL=rc|Hn*b_d%BkE*{x{3DT_>S7PYf&7N%OANv<6af|QBL26NIdQoPcUP*p_wnGUpUX${R@=Mb*;2{G_Z!^Af`F}eQk$9qw zS9qDltFXgg*OpG${rf}6!{b|S$|f}cqxORc!-meb3YHx&|4chPSuyeBWPc}RwuuEd zRklB{{-LxR7`03nzd!qw<9wy9xBc8EQ^Uj(t~DROiy>HT;Z5rDm!Xq5#EflK`X~^8y2>t78^3Y>(n|NDbo9FqSBM+XZ3a@`f-Ua( X@v2^*cbh$5beXYjy52thomsX39o|<- literal 0 HcmV?d00001 diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/sign-rsa4096.sig b/minimal-examples/crypto/minimal-crypto-cose-sign/sign-rsa4096.sig new file mode 100644 index 0000000000000000000000000000000000000000..a233d68cf0e51b17833f5e91faa40c1e1c8d2636 GIT binary patch literal 550 zcmV+>0@?l8VuV0IOmt;)bRckHd2DZCWPyXB0XYEzp#(>Ab73?vIW}1W0FR88t#IHH z6Ar+13}7r&Sc-p-@Ou;Gg%ZPNVH90vONl^P&Mq&0Gko5@D&_sxQ!z_w*CPE3d{`~$E2-qbMAKR znO(e#I%%w&tKI)qO{?^p&?};`4qzV2_J!Hcs*Vb8nkpXg69VZ=mCHyJtSX=E(lIbJ zX{5D)_1wSLfyHEmOck2a`~5ZjkOObY{J%CW)KZ8D>SH z+#}H~$~o<_TPr#1{+@R90PfC>T`BcZAZ!EF(6>(v?y)z)2tqH_wh9_Z!-Sg-+^u@i zPHEa9yySJBrUE%5HC1)tAVm>|8#nPwZ~3#Mo#n6oTyrJGvL5AvG9h5<9^712xE*o} zV?PF<%M*d@WVo7L4y!=5jPnBYnDZuco0gOA%_&|U9vnXxhTziqdKQS|gjC;Ke_?x2 z-N0lMQ=B`@f!ECY*S%n3EvHAG=w{MC-7@x&cR!^HR`o%zkR~`Q(9R!ZZRLC o%r \ No newline at end of file diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/sign1_pass03.sig b/minimal-examples/crypto/minimal-crypto-cose-sign/sign1_pass03.sig new file mode 100644 index 000000000..a10fc966e --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/sign1_pass03.sig @@ -0,0 +1 @@ +C&B11TThis is the content.X@>LFZZ4k#Տ\1Z~*2J4V*"4DT~; E6 \ No newline at end of file diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass01.sig b/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass01.sig new file mode 100644 index 000000000..268b08618 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass01.sig @@ -0,0 +1 @@ +bATThis is the content.C&B11X@⮯ iѝnR|](,].قETLEHj+:/ diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass02.sig b/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass02.sig new file mode 100644 index 000000000..b80f7a26f --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass02.sig @@ -0,0 +1 @@ +b@TThis is the content.C&B11X@˸پMkOZ$2>tQ?&rٓDvG1W̨k diff --git a/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass03.sig b/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass03.sig new file mode 100644 index 000000000..60d8f0394 --- /dev/null +++ b/minimal-examples/crypto/minimal-crypto-cose-sign/sign_pass03.sig @@ -0,0 +1 @@ +@TThis is the content.C&B11X@⮯ iѝnR|](,].قETLEHj+:/ diff --git a/scripts/client-ca/create-client-cert.sh b/scripts/client-ca/create-client-cert.sh index cee8cbd31..1546d83fa 100755 --- a/scripts/client-ca/create-client-cert.sh +++ b/scripts/client-ca/create-client-cert.sh @@ -5,8 +5,9 @@ if [ -z "$1" ] ; then exit 1 fi +mkdir -p certs openssl genrsa -out $1.key 4096 && \ -printf "\\n\\n\\n\\n\\nlocalhost\\n\\n1234\\n\\n" | \ +printf "\\n\\n\\n\\n\\n$1\\n\\n1234\\n\\n" | \ openssl req -config tmp.cnf -new -key $1.key -out $1.csr && \ openssl ca -config tmp.cnf \ -keyfile ca.key \ diff --git a/scripts/client-ca/create-server-cert.sh b/scripts/client-ca/create-server-cert.sh index 46a159000..fd5dc2024 100755 --- a/scripts/client-ca/create-server-cert.sh +++ b/scripts/client-ca/create-server-cert.sh @@ -5,6 +5,7 @@ if [ -z "$1" ] ; then exit 1 fi +mkdir -p certs openssl genrsa -out $1.key 4096 && \ printf "\\n\\n\\n\\n\\nlocalhost\\n\\n1234\\n\\n" | \ openssl req -config tmp.cnf -new -key $1.key -out $1.csr && \