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
This commit is contained in:
Andy Green 2021-07-13 05:18:04 +01:00
parent dcaa0013b4
commit 4db2ff872b
61 changed files with 9185 additions and 671 deletions

View File

@ -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

View File

@ -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)

272
READMEs/README.cbor-cose.md Normal file
View File

@ -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);
```

View File

@ -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

View File

@ -616,8 +616,11 @@ struct lws;
#if defined(LWS_WITH_FILE_OPS)
#include <libwebsockets/lws-vfs.h>
#endif
#include <libwebsockets/lws-gencrypto.h>
#include <libwebsockets/lws-lejp.h>
#include <libwebsockets/lws-lecp.h>
#include <libwebsockets/lws-cose.h>
#include <libwebsockets/lws-struct.h>
#include <libwebsockets/lws-threadpool.h>
#include <libwebsockets/lws-tokenize.h>
@ -647,7 +650,6 @@ struct lws;
#include <mbedtls/sha512.h>
#endif
#include <libwebsockets/lws-gencrypto.h>
#include <libwebsockets/lws-genhash.h>
#include <libwebsockets/lws-genrsa.h>
#include <libwebsockets/lws-genaes.h>

View File

@ -0,0 +1,511 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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);
//@}

View File

@ -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;
/**

View File

@ -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
*

View File

@ -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);

View File

@ -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;
};

View File

@ -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)

View File

@ -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

39
lib/cose/CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# 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()

1188
lib/cose/cose_key.c Normal file

File diff suppressed because it is too large Load Diff

536
lib/cose/cose_sign.c Normal file
View File

@ -0,0 +1,536 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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);
}

271
lib/cose/cose_sign_alg.c Normal file
View File

@ -0,0 +1,271 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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);
}

1051
lib/cose/cose_validate.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,274 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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);
}

148
lib/cose/private-lib-cose.h Normal file
View File

@ -0,0 +1,148 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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);

View File

@ -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

649
lib/jose/jwk/jose_key.c Normal file
View File

@ -0,0 +1,649 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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 <fcntl.h>
#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;
}

View File

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* 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 <fcntl.h>
#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;
}

View File

@ -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;

View File

@ -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];

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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()

View File

@ -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 <loglevel>|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
```

View File

@ -0,0 +1,931 @@
/*
* lws-api-test-jose - RFC8152 cose_key tests
*
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* 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 <libwebsockets.h>
#include <stdlib.h>
#include <fcntl.h>
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;
}

View File

@ -0,0 +1,50 @@
/*
* lws-api-test-cose
*
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*/
#include <libwebsockets.h>
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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()

View File

@ -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 \<keyset filepath\>|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
```

View File

@ -0,0 +1,313 @@
/*
* lws-minimal-crypto-cose-key
*
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*/
#include <libwebsockets.h>
#include <sys/select.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
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;
}

View File

@ -0,0 +1 @@
Ò„C¡B11TThis is the content.X@×Ë8ØØéD¨Úqç²XɽÊa5÷®Ûî• gƒ~3½6ÁP2jæ'UƽŽT ><EFBFBD>×Ò%èÛr¸

View File

@ -0,0 +1 @@
„C¡B11TThis is the content.X@Ž³>L£FZ°Z¬4Ìk#Õ<>ï\1ÄÒZ®ð°~*ù¢ª2áJ¸4ÜVí*"4DT~ñ; å¤ÃEÊË6

View File

@ -0,0 +1 @@
Øb„A  TThis is the content.<EFBFBD>ƒC¡B11X@â®¯Ô <EFBFBD>þnR|]ôä(,¾û]Ëô¯.Ù¬E¬˜¸TL<54>EÞ<1E>·ÃÓHþj+˜õ:ý/ ó

View File

@ -0,0 +1 @@
Øb„@ TThis is the content.<EFBFBD>ƒC¡B11X@˸ÚÙ¾¯¸<C2AF>á¤MûÂkíò©OËZˆ$2¿öÖ>õtQØ?¢Ëö&rëôÇÙ“°ôÂDvGØ1ºW̨k“

View File

@ -0,0 +1 @@
„@ TThis is the content.<EFBFBD>ƒC¡B11X@â®¯Ô <EFBFBD>þnR|]ôä(,¾û]Ëô¯.Ù¬E¬˜¸TL<54>EÞ<1E>·ÃÓHþj+˜õ:ý/ ó

View File

@ -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()

View File

@ -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 <keyset filepath>|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|
```

View File

@ -0,0 +1,406 @@
/*
* lws-minimal-crypto-cose-sign
*
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*/
#include <libwebsockets.h>
#include <sys/types.h>
#include <fcntl.h>
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 <key set file> 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;
}

View File

@ -0,0 +1 @@
The Test Payload

View File

@ -0,0 +1 @@
Ò„C¡B11TThis is the content.X@×Ë8ØØéD¨Úqç²XɽÊa5÷®Ûî• gƒ~3½6ÁP2jæ'UƽŽT ><EFBFBD>×Ò%èÛr¸

View File

@ -0,0 +1 @@
„C¡B11TThis is the content.X@Ž³>L£FZ°Z¬4Ìk#Õ<>ï\1ÄÒZ®ð°~*ù¢ª2áJ¸4ÜVí*"4DT~ñ; å¤ÃEÊË6

View File

@ -0,0 +1 @@
Øb„A  TThis is the content.<EFBFBD>ƒC¡B11X@â®¯Ô <EFBFBD>þnR|]ôä(,¾û]Ëô¯.Ù¬E¬˜¸TL<54>EÞ<1E>·ÃÓHþj+˜õ:ý/ ó

View File

@ -0,0 +1 @@
Øb„@ TThis is the content.<EFBFBD>ƒC¡B11X@˸ÚÙ¾¯¸<C2AF>á¤MûÂkíò©OËZˆ$2¿öÖ>õtQØ?¢Ëö&rëôÇÙ“°ôÂDvGØ1ºW̨k“

View File

@ -0,0 +1 @@
„@ TThis is the content.<EFBFBD>ƒC¡B11X@â®¯Ô <EFBFBD>þnR|]ôä(,¾û]Ëô¯.Ù¬E¬˜¸TL<54>EÞ<1E>·ÃÓHþj+˜õ:ý/ ó

View File

@ -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 \

View File

@ -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 && \