mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lws-x509: validation functions
This commit is contained in:
parent
21889b53f7
commit
0adc845507
35 changed files with 2025 additions and 615 deletions
|
@ -1014,6 +1014,7 @@ if (LWS_WITH_SSL)
|
|||
if (LWS_WITH_MBEDTLS)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/mbedtls/ssl.c
|
||||
lib/tls/mbedtls/x509.c
|
||||
)
|
||||
if (LWS_WITH_GENCRYPTO)
|
||||
list(APPEND SOURCES
|
||||
|
@ -1028,6 +1029,7 @@ if (LWS_WITH_SSL)
|
|||
else()
|
||||
list(APPEND SOURCES
|
||||
lib/tls/openssl/ssl.c
|
||||
lib/tls/openssl/x509.c
|
||||
)
|
||||
if (LWS_WITH_GENCRYPTO)
|
||||
list(APPEND SOURCES
|
||||
|
|
13
README.md
13
README.md
|
@ -18,12 +18,25 @@ News
|
|||
|
||||
## New features on master
|
||||
|
||||
- New Crypto-agile APIs + JOSE / JWS / JWE / JWK support... apis work exactly
|
||||
the same with OpenSSL or mbedTLS tls library backends, and allow key cycling
|
||||
and crypto algorithm changes while allowing for grace periods
|
||||
|
||||
[README.crypto-apis](https://libwebsockets.org/git/libwebsockets/tree/READMEs/README.crypto-apis.md)
|
||||
|
||||
- CMake config simplification for crypto: `-DLWS_WITH_GENCRYPTO=1` for all
|
||||
generic cipher and hash apis built (which work the same on mbedtls and
|
||||
OpenSSL transparently), and `-DLWS_WITH_JOSE=1` for all JOSE, JWK, JWS
|
||||
and JWE support built (which use gencrypto and so also work the same
|
||||
regardless of tls library backend).
|
||||
|
||||
- **`x.509`** - new generic x509 api allows PEM-based certificate and key
|
||||
trust relationship verification, and conversion between x.509 keys and
|
||||
JWK. Works for EC and RSA keys, and on mbedtls and OpenSSl the same.
|
||||
|
||||
[x.509 api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-x509.h),
|
||||
[x.509 minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-x509)
|
||||
|
||||
- **`JWE`** - JWE (RFC7516) Algorithms with CI tests:
|
||||
|
||||
|Key Encryption|Payload authentication + crypt|Enc + Dec Support|
|
||||
|
|
|
@ -28,6 +28,44 @@ once it's working, you literally just change your JSON defining the keys and
|
|||
JWE or JWS algorithm. (It's up to you to define your policy for which
|
||||
combinations are acceptable by querying the parsed JW structs).
|
||||
|
||||
## Crypto supported in generic layer
|
||||
|
||||
### Generic Hash
|
||||
|
||||
- SHA1
|
||||
- SHA256
|
||||
- SHA384
|
||||
- SHA512
|
||||
|
||||
### Generic HMAC
|
||||
|
||||
- SHA256
|
||||
- SHA384
|
||||
- SHA512
|
||||
|
||||
### Generic AES
|
||||
|
||||
- CBC
|
||||
- CFB128
|
||||
- CFB8
|
||||
- CTR
|
||||
- ECB
|
||||
- OFB
|
||||
- XTS
|
||||
- GCM
|
||||
- KW (Key Wrap)
|
||||
|
||||
### Generic RSA
|
||||
|
||||
- PKCS 1.5
|
||||
- OAEP / PSS
|
||||
|
||||
### Generic EC
|
||||
|
||||
- ECDH
|
||||
- ECDSA
|
||||
- P256 / P384 / P521 (sic) curves
|
||||
|
||||
## Using the generic layer
|
||||
|
||||
All the necessary includes are part of `libwebsockets.h`.
|
||||
|
@ -35,11 +73,12 @@ All the necessary includes are part of `libwebsockets.h`.
|
|||
Enable `-DLWS_WITH_GENCRYPTO=1` at cmake.
|
||||
|
||||
|api|header|Functionality|
|
||||
|---|---|---|---|
|
||||
|---|---|---|
|
||||
|genhash|[./include/libwebsockets/lws-genhash.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genhash.h)|Provides SHA1 + SHA2 hashes and hmac|
|
||||
|genrsa|[./include/libwebsockets/lws-genrsa.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genrsa.h)|Provides RSA encryption, decryption, signing, verification, key generation and creation|
|
||||
|genaes|[./include/libwebsockets/lws-genaes.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genaes.h)|Provides AES in all common variants for encryption and decryption|
|
||||
|genec|[./include/libwebsockets/lws-genec.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genec.h)|Provides Elliptic Curve for encryption, decryption, signing, verification, key generation and creation|
|
||||
|x509|[./include/libwebsockets/lws-x509.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-x509.h)|Apis for X.509 Certificate loading, parsing, and stack verification, plus JWK key extraction from PEM X.509 certificate / private key|
|
||||
|
||||
Unit tests for these apis, which serve as usage examples, can be found in [./minimal-examples/api-tests/api-test-gencrypto](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-gencrypto)
|
||||
|
||||
|
@ -62,19 +101,65 @@ store arbitrary octets that make up the key element's binary representation.
|
|||
|
||||
## Using the JOSE layer
|
||||
|
||||
The JOSE (JWK / JWS / JWE) stuff is a crypto-agile JSON-based layer
|
||||
that uses the gencrypto support underneath.
|
||||
|
||||
"Crypto Agility" means the JSON structs include information about the
|
||||
algorithms and ciphers used in that particular object, making it easy to
|
||||
upgrade system crypto strength or cycle keys over time while supporting a
|
||||
transitional period where the old and new keys or algorithms + ciphers
|
||||
are also valid.
|
||||
|
||||
Uniquely lws generic support means the JOSE stuff also has "tls library
|
||||
agility", code written to the lws generic or JOSE apis is completely unchanged
|
||||
even if the underlying tls library changes between OpenSSL and mbedtls, meaning
|
||||
sharing code between server and client sides is painless.
|
||||
|
||||
All the necessary includes are part of `libwebsockets.h`.
|
||||
|
||||
Enable `-DLWS_WITH_JOSE=1` at CMake.
|
||||
|
||||
|api|header|Functionality|
|
||||
|---|---|---|---|
|
||||
|---|---|---|
|
||||
|JOSE|[./include/libwebsockets/lws-jose.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jose.h)|Provides crypto agility for JWS / JWE|
|
||||
|JWE|[./include/libwebsockets/lws-jwe.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jwe.h)|Provides Encryption and Decryption services for RFC7516 JWE JSON|
|
||||
|JWS|[./include/libwebsockets/lws-jws.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jws.h)|Provides signature and verifcation services for RFC7515 JWS JSON|
|
||||
|JWK|[./include/libwebsockets/lws-jwk.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jwk.h)|Provides signature and verifcation services for RFC7517 JWK JSON, both "keys" arrays and singletons|
|
||||
|
||||
Minimal examples are provided in the form of commandline tools for JWK / JWS / JWE / x509 handling:
|
||||
|
||||
- [JWK minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwk)
|
||||
- [JWS minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jws)
|
||||
- [JWE minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
|
||||
- [X509 minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-x509)
|
||||
|
||||
Unit tests for these apis, which serve as usage examples, can be found in [./minimal-examples/api-tests/api-test-jose](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-jose)
|
||||
|
||||
## Crypto supported in the JOSE layer
|
||||
|
||||
The JOSE RFCs define specific short names for different algorithms
|
||||
|
||||
### JWS
|
||||
|
||||
|JSOE name|Hash|Signature|
|
||||
---|---|---
|
||||
|RS256, RS384, RS512|SHA256/384/512|RSA
|
||||
|ES256, ES384, ES521|SHA256/384/512|EC
|
||||
|
||||
### JWE
|
||||
|
||||
|Key Encryption|Payload authentication + crypt|
|
||||
|---|---|
|
||||
|`RSAES-PKCS1-v1.5` 2048b & 4096b|`AES_128_CBC_HMAC_SHA_256`|
|
||||
|`RSAES-PKCS1-v1.5` 2048b|`AES_192_CBC_HMAC_SHA_384`|
|
||||
|`RSAES-PKCS1-v1.5` 2048b|`AES_256_CBC_HMAC_SHA_512`|
|
||||
|`RSAES-OAEP`|`AES_256_GCM`|
|
||||
|`AES128KW`, `AES192KW`, `AES256KW`|`AES_128_CBC_HMAC_SHA_256`|
|
||||
|`AES128KW`, `AES192KW`, `AES256KW`|`AES_192_CBC_HMAC_SHA_384`|
|
||||
|`AES128KW`, `AES192KW`, `AES256KW`|`AES_256_CBC_HMAC_SHA_512`|
|
||||
|`ECDH-ES` (P-256/384/521 key)|`AES_128/192/256_GCM`|
|
||||
|`ECDH-ES+A128/192/256KW` (P-256/384/521 key)|`AES_128/192/256_GCM`|
|
||||
|
||||
### Keys in the JOSE layer
|
||||
|
||||
Keys in the JOSE layer use a `struct lws_jwk`, this contains two arrays of
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 507 KiB After Width: | Height: | Size: 626 KiB |
|
@ -90,7 +90,7 @@ enum lws_gencrypto_aes_tok {
|
|||
|
||||
struct lws_gencrypto_keyelem {
|
||||
uint8_t *buf;
|
||||
uint16_t len;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ enum enum_lws_dh_side {
|
|||
struct lws_ec_curves {
|
||||
const char *name;
|
||||
int tls_lib_nid;
|
||||
short key_bytes;
|
||||
uint16_t key_bytes;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -81,6 +81,9 @@ lws_jwe_render_compact(struct lws_jwe *jwe, char *out, size_t out_len);
|
|||
LWS_VISIBLE int
|
||||
lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_jwe_json_parse(struct lws_jwe *jwe, const uint8_t *buf, int len,
|
||||
char *temp, int *temp_len);
|
||||
|
||||
/**
|
||||
* lws_jwe_auth_and_decrypt() - confirm and decrypt JWE
|
||||
|
|
|
@ -59,9 +59,11 @@ enum enum_jws_sig_elements {
|
|||
|
||||
struct lws_jws_map {
|
||||
const char *buf[LWS_JWS_MAX_COMPACT_BLOCKS];
|
||||
uint16_t len[LWS_JWS_MAX_COMPACT_BLOCKS];
|
||||
uint32_t len[LWS_JWS_MAX_COMPACT_BLOCKS];
|
||||
};
|
||||
|
||||
#define LWS_JWS_MAX_SIGS 3
|
||||
|
||||
struct lws_jws {
|
||||
struct lws_jwk *jwk; /* the struct lws_jwk containing the signing key */
|
||||
struct lws_context *context; /* the lws context (used to get random) */
|
||||
|
@ -205,11 +207,17 @@ LWS_VISIBLE LWS_EXTERN int
|
|||
lws_jws_compact_decode(const char *in, int len, struct lws_jws_map *map,
|
||||
struct lws_jws_map *map_b64, char *out, int *out_len);
|
||||
|
||||
LWS_VISIBLE int
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_jws_compact_encode(struct lws_jws_map *map_b64, /* b64-encoded */
|
||||
const struct lws_jws_map *map, /* non-b64 */
|
||||
char *buf, int *out_len);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_jws_sig_confirm_json(const char *in, size_t len,
|
||||
struct lws_jws *jws, struct lws_jwk *jwk,
|
||||
struct lws_context *context,
|
||||
char *temp, int *temp_len);
|
||||
|
||||
/**
|
||||
* lws_jws_write_flattened_json() - create flattened JSON sig
|
||||
*
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -65,6 +65,107 @@ union lws_tls_cert_info_results {
|
|||
} ns;
|
||||
};
|
||||
|
||||
struct lws_x509_cert;
|
||||
struct lws_jwk;
|
||||
|
||||
/**
|
||||
* lws_x509_create() - Allocate an lws_x509_cert object
|
||||
*
|
||||
* \param x509: pointer to lws_x509_cert pointer to be set to allocated object
|
||||
*
|
||||
* Allocates an lws_x509_cert object and set *x509 to point to it.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_x509_create(struct lws_x509_cert **x509);
|
||||
|
||||
/**
|
||||
* lws_x509_parse_from_pem() - Read one or more x509 certs in PEM format from memory
|
||||
*
|
||||
* \param x509: pointer to lws_x509_cert object
|
||||
* \param pem: pointer to PEM format content
|
||||
* \param len: length of PEM format content
|
||||
*
|
||||
* Parses PEM certificates in memory into a native x509 representation for the
|
||||
* TLS library. If there are multiple PEM certs concatenated, they are all
|
||||
* read into the same object and exist as a "chain".
|
||||
*
|
||||
* IMPORTANT for compatibility with mbedtls, the last used byte of \p pem
|
||||
* must be '\0' and the \p len must include it.
|
||||
*
|
||||
* Returns 0 if all went OK.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len);
|
||||
|
||||
/**
|
||||
* lws_x509_verify() - Validate signing relationship between one or more certs
|
||||
* and a trusted CA cert
|
||||
*
|
||||
* \param x509: pointer to lws_x509_cert object, may contain multiple
|
||||
* \param trusted: a single, trusted cert object that we are checking for
|
||||
* \param common_name: NULL, or required CN (Common Name) of \p x509
|
||||
*
|
||||
* Returns 0 if the cert or certs in \p x509 represent a complete chain that is
|
||||
* ultimately signed by the cert in \p trusted. Returns nonzero if that's not
|
||||
* the case.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted,
|
||||
const char *common_name);
|
||||
|
||||
/**
|
||||
* lws_x509_public_to_jwk() - Copy the public key out of a cert and into a JWK
|
||||
*
|
||||
* \param jwk: pointer to the jwk to initialize and set to the public key
|
||||
* \param x509: pointer to lws_x509_cert object that has the public key
|
||||
* \param curves: NULL to disallow EC, else a comma-separated list of valid
|
||||
* curves using the JWA naming, eg, "P-256,P-384,P-521".
|
||||
* \param rsabits: minimum number of RSA bits required in the cert if RSA
|
||||
*
|
||||
* Returns 0 if JWK was set to the certificate public key correctly and the
|
||||
* curve / the RSA key size was acceptable. Automatically produces an RSA or
|
||||
* EC JWK depending on what the cert had.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509,
|
||||
const char *curves, int rsabits);
|
||||
|
||||
/**
|
||||
* lws_x509_jwk_privkey_pem() - Copy a private key PEM into a jwk that has the
|
||||
* public part already
|
||||
*
|
||||
* \param jwk: pointer to the jwk to initialize and set to the public key
|
||||
* \param pem: pointer to PEM private key in memory
|
||||
* \param len: length of PEM private key in memory
|
||||
* \param passphrase: NULL or passphrase needed to decrypt private key
|
||||
*
|
||||
* IMPORTANT for compatibility with mbedtls, the last used byte of \p pem
|
||||
* must be '\0' and the \p len must include it.
|
||||
*
|
||||
* Returns 0 if the private key was successfully added to the JWK, else
|
||||
* nonzero if failed.
|
||||
*
|
||||
* The PEM image in memory is zeroed down on both successful and failed exits.
|
||||
* The caller should take care to zero down passphrase if used.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len,
|
||||
const char *passphrase);
|
||||
|
||||
/**
|
||||
* lws_x509_destroy() - Destroy a previously allocated lws_x509_cert object
|
||||
*
|
||||
* \param x509: pointer to lws_x509_cert pointer
|
||||
*
|
||||
* Deallocates an lws_x509_cert object and sets its pointer to NULL.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_x509_destroy(struct lws_x509_cert **x509);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len);
|
||||
|
||||
/**
|
||||
* lws_tls_peer_cert_info() - get information from the peer's TLS cert
|
||||
*
|
||||
|
|
|
@ -607,7 +607,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
|
||||
#ifdef LWS_WITH_UNIX_SOCK
|
||||
if (LWS_UNIX_SOCK_ENABLED(vh)) {
|
||||
lwsl_notice("Creating Vhost '%s' path \"%s\", %d protocols\n",
|
||||
lwsl_info("Creating Vhost '%s' path \"%s\", %d protocols\n",
|
||||
vh->name, vh->iface, vh->count_protocols);
|
||||
} else
|
||||
#endif
|
||||
|
@ -623,7 +623,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
lws_snprintf(buf, sizeof(buf), "port %u", info->port);
|
||||
break;
|
||||
}
|
||||
lwsl_notice("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n",
|
||||
lwsl_info("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n",
|
||||
vh->name, buf, vh->count_protocols,
|
||||
LWS_IPV6_ENABLED(vh) ? "on" : "off");
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ lws_jwe_encrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *cek,
|
|||
|
||||
/* Caller must have prepared space for the results */
|
||||
|
||||
if (jwe->jws.map.len[LJWE_ATAG] != hlen / 2) {
|
||||
if (jwe->jws.map.len[LJWE_ATAG] != (unsigned int)hlen / 2) {
|
||||
lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
|
||||
hlen / 2, jwe->jws.map.len[LJWE_ATAG]);
|
||||
return -1;
|
||||
|
@ -165,7 +165,7 @@ lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jwe *jwe, uint8_t *enc_cek,
|
|||
|
||||
/* Some sanity checks on what came in */
|
||||
|
||||
if (jwe->jws.map.len[LJWE_ATAG] != hlen / 2) {
|
||||
if (jwe->jws.map.len[LJWE_ATAG] != (unsigned int)hlen / 2) {
|
||||
lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
|
||||
hlen / 2, jwe->jws.map.len[LJWE_ATAG]);
|
||||
return -1;
|
||||
|
|
|
@ -541,7 +541,7 @@ lws_jwe_auth_and_decrypt_ecdh(struct lws_jwe *jwe)
|
|||
|
||||
/* Confirm space for EKEY */
|
||||
|
||||
if (jwe->jws.map.len[LJWE_EKEY] < enc_hlen) {
|
||||
if (jwe->jws.map.len[LJWE_EKEY] < (unsigned int)enc_hlen) {
|
||||
lwsl_err("%s: missing EKEY\n", __func__);
|
||||
|
||||
goto bail;
|
||||
|
|
|
@ -26,47 +26,40 @@
|
|||
#include "jose/private.h"
|
||||
#include "jose/jwe/private.h"
|
||||
|
||||
#if 0
|
||||
static const char * const jwe_complete_tokens[] = {
|
||||
/*
|
||||
* Currently only support flattened or compact (implicitly single signature)
|
||||
*/
|
||||
|
||||
static const char * const jwe_json[] = {
|
||||
"protected",
|
||||
"recipients[].header",
|
||||
"recipients[].header.alg",
|
||||
"recipients[].header.kid",
|
||||
"recipients[].encrypted_key",
|
||||
"iv",
|
||||
"ciphertext",
|
||||
"tag",
|
||||
"encrypted_key"
|
||||
};
|
||||
|
||||
enum enum_jwe_complete_tokens {
|
||||
LWS_EJCT_PROTECTED,
|
||||
LWS_EJCT_HEADER,
|
||||
LWS_EJCT_HEADER_ALG,
|
||||
LWS_EJCT_HEADER_KID,
|
||||
LWS_EJCT_RECIP_ENC_KEY,
|
||||
LWS_EJCT_IV,
|
||||
LWS_EJCT_CIPHERTEXT,
|
||||
LWS_EJCT_TAG,
|
||||
LWS_EJCT_RECIP_ENC_KEY,
|
||||
};
|
||||
|
||||
struct complete_cb_args {
|
||||
struct lws_jws_map *map;
|
||||
struct lws_jws_map *map_b64;
|
||||
char *out;
|
||||
int out_len;
|
||||
/* parse a JWS complete or flattened JSON object */
|
||||
|
||||
struct jwe_cb_args {
|
||||
struct lws_jws *jws;
|
||||
|
||||
char *temp;
|
||||
int *temp_len;
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
do_map(struct complete_cb_args *args, int index, char *b64, int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static signed char
|
||||
lws_jwe_parse_complete_cb(struct lejp_ctx *ctx, char reason)
|
||||
lws_jwe_json_cb(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct complete_cb_args *args = (struct complete_cb_args *)ctx->user;
|
||||
struct jwe_cb_args *args = (struct jwe_cb_args *)ctx->user;
|
||||
int n, m;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
@ -75,49 +68,95 @@ lws_jwe_parse_complete_cb(struct lejp_ctx *ctx, char reason)
|
|||
|
||||
/* strings */
|
||||
|
||||
case LWS_EJCT_PROTECTED:
|
||||
case LWS_EJCT_HEADER:
|
||||
case LWS_EJCT_HEADER_ALG:
|
||||
case LWS_EJCT_HEADER_KID:
|
||||
case LWS_EJCT_RECIP_ENC_KEY:
|
||||
case LWS_EJCT_IV:
|
||||
case LWS_EJCT_CIPHERTEXT:
|
||||
case LWS_EJCT_TAG:
|
||||
case LWS_EJCT_PROTECTED: /* base64u: JOSE: must contain 'alg' */
|
||||
m = LJWS_JOSE;
|
||||
goto append_string;
|
||||
case LWS_EJCT_IV: /* base64u */
|
||||
m = LJWE_IV;
|
||||
goto append_string;
|
||||
case LWS_EJCT_CIPHERTEXT: /* base64u */
|
||||
m = LJWE_CTXT;
|
||||
goto append_string;
|
||||
case LWS_EJCT_TAG: /* base64u */
|
||||
m = LJWE_ATAG;
|
||||
goto append_string;
|
||||
case LWS_EJCT_RECIP_ENC_KEY: /* base64u */
|
||||
m = LJWE_EKEY;
|
||||
goto append_string;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
append_string:
|
||||
|
||||
if (*args->temp_len < ctx->npos) {
|
||||
lwsl_err("%s: out of parsing space\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We keep both b64u and decoded in temp mapped using map / map_b64,
|
||||
* the jws signature is actually over the b64 content not the plaintext,
|
||||
* and we can't do it until we see the protected alg.
|
||||
*/
|
||||
|
||||
if (!args->jws->map_b64.buf[m]) {
|
||||
args->jws->map_b64.buf[m] = args->temp;
|
||||
args->jws->map_b64.len[m] = 0;
|
||||
}
|
||||
|
||||
memcpy(args->temp, ctx->buf, ctx->npos);
|
||||
args->temp += ctx->npos;
|
||||
*args->temp_len -= ctx->npos;
|
||||
args->jws->map_b64.len[m] += ctx->npos;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_END) {
|
||||
args->jws->map.buf[m] = args->temp;
|
||||
|
||||
n = lws_b64_decode_string_len(
|
||||
(const char *)args->jws->map_b64.buf[m],
|
||||
args->jws->map_b64.len[m],
|
||||
(char *)args->temp, *args->temp_len);
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: b64 decode failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
args->temp += n;
|
||||
*args->temp_len -= n;
|
||||
args->jws->map.len[m] = n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_complete_decode(const char *json_in, int len,
|
||||
struct lws_jws_map *map,
|
||||
struct lws_jws_map *map_b64, char *out,
|
||||
int out_len)
|
||||
int
|
||||
lws_jwe_json_parse(struct lws_jwe *jwe, const uint8_t *buf, int len,
|
||||
char *temp, int *temp_len)
|
||||
{
|
||||
|
||||
struct complete_cb_args args;
|
||||
struct jwe_cb_args args;
|
||||
struct lejp_ctx jctx;
|
||||
int blocks, n, m = 0;
|
||||
int m = 0;
|
||||
|
||||
if (!map_b64)
|
||||
map_b64 = map;
|
||||
args.jws = &jwe->jws;
|
||||
args.temp = temp;
|
||||
args.temp_len = temp_len;
|
||||
|
||||
memset(map_b64, 0, sizeof(*map_b64));
|
||||
memset(map, 0, sizeof(*map));
|
||||
lejp_construct(&jctx, lws_jwe_json_cb, &args, jwe_json,
|
||||
LWS_ARRAY_SIZE(jwe_json));
|
||||
|
||||
args.map = map;
|
||||
args.map_b64 = map_b64;
|
||||
args.out = out;
|
||||
args.out_len = out_len;
|
||||
|
||||
lejp_construct(&jctx, lws_jwe_parse_complete_cb, &args,
|
||||
jwe_complete_tokens,
|
||||
LWS_ARRAY_SIZE(jwe_complete_tokens));
|
||||
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)json_in, len);
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, len);
|
||||
lejp_destruct(&jctx);
|
||||
if (m < 0) {
|
||||
lwsl_notice("%s: parse returned %d\n", __func__, m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
lws_jwe_init(struct lws_jwe *jwe, struct lws_context *context)
|
||||
|
@ -294,6 +333,14 @@ lws_jwe_auth_and_decrypt(struct lws_jwe *jwe, char *temp, int *temp_len)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (!jwe->jose.alg) {
|
||||
lwsl_err("%s: no jose.alg: %.*s\n", __func__,
|
||||
jwe->jws.map.len[LJWS_JOSE],
|
||||
jwe->jws.map.buf[LJWS_JOSE]);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
valid_aescbc_hmac = jwe->jose.enc_alg &&
|
||||
jwe->jose.enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC &&
|
||||
(jwe->jose.enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 ||
|
||||
|
@ -673,8 +720,8 @@ static int protected_idx[] = {
|
|||
LWS_VISIBLE int
|
||||
lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len)
|
||||
{
|
||||
char buf[3072], *p1, *end1;
|
||||
int m, n, jlen;
|
||||
char buf[3072], *p1, *end1, protected[128];
|
||||
int m, n, jlen, plen;
|
||||
|
||||
jlen = lws_jose_render(&jwe->jose, jwe->jws.jwk, buf, sizeof(buf));
|
||||
if (jlen < 0) {
|
||||
|
@ -694,9 +741,13 @@ lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len)
|
|||
* The protected header is b64url encoding of the JOSE header part
|
||||
*/
|
||||
|
||||
plen = lws_snprintf(protected, sizeof(protected),
|
||||
"{\"alg\":\"%s\",\"enc\":\"%s\"}",
|
||||
jwe->jose.alg->alg, jwe->jose.enc_alg->alg);
|
||||
|
||||
p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\"");
|
||||
jwe->jws.map_b64.buf[LJWS_JOSE] = p1;
|
||||
n = lws_jws_base64_enc(buf, jlen, p1, end1 - p1);
|
||||
n = lws_jws_base64_enc(protected, plen, p1, end1 - p1);
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: failed to encode protected\n", __func__);
|
||||
goto bail;
|
||||
|
@ -710,7 +761,7 @@ lws_jwe_render_flattened(struct lws_jwe *jwe, char *out, size_t out_len)
|
|||
|
||||
for (m = 0; m < (int)LWS_ARRAY_SIZE(protected_en); m++)
|
||||
if (jwe->jws.map.buf[protected_idx[m]]) {
|
||||
p1 += lws_snprintf(p1, end1 - p1, ",\"%s\":\"",
|
||||
p1 += lws_snprintf(p1, end1 - p1, ",\n\"%s\":\"",
|
||||
protected_en[m]);
|
||||
//jwe->jws.map_b64.buf[protected_idx[m]] = p1;
|
||||
n = lws_jws_base64_enc(jwe->jws.map.buf[protected_idx[m]],
|
||||
|
|
|
@ -562,7 +562,7 @@ lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len)
|
|||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
|
||||
enum lws_gencrypto_kty kty, int bits, const char *curve)
|
||||
enum lws_gencrypto_kty kty, int bits, const char *curve)
|
||||
{
|
||||
int n;
|
||||
|
||||
|
|
|
@ -485,7 +485,7 @@ lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
|
|||
case LJJHI_ZIP: /* JWE only: Optional: string ("DEF"=deflate) */
|
||||
if (jose->e[n].buf) {
|
||||
out += lws_snprintf(out, end - out,
|
||||
"%c\"%s\":\"%s\"", sub ? ',' : ' ',
|
||||
"%s\"%s\":\"%s\"", sub ? ",\n" : "",
|
||||
jws_jose[n], jose->e[n].buf);
|
||||
sub = 1;
|
||||
}
|
||||
|
@ -500,7 +500,7 @@ lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
|
|||
case LJJHI_P2S: /* Additional arg for JWE PBES2: b64url: salt */
|
||||
if (jose->e[n].buf) {
|
||||
out += lws_snprintf(out, end - out,
|
||||
"%c\"%s\":\"", sub ? ',' : ' ',
|
||||
"%s\"%s\":\"", sub ? ",\n" : "",
|
||||
jws_jose[n]);
|
||||
sub = 1;
|
||||
m = lws_b64_encode_string_url((const char *)
|
||||
|
@ -519,7 +519,7 @@ lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
|
|||
case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */
|
||||
if (jose->e[n].buf) {
|
||||
out += lws_snprintf(out, end - out,
|
||||
"%c\"%s\":\"", sub ? ',' : ' ',
|
||||
"%s\"%s\":\"", sub ? ",\n" : "",
|
||||
jws_jose[n]);
|
||||
sub = 1;
|
||||
m = lws_b64_encode_string((const char *)
|
||||
|
@ -536,11 +536,11 @@ lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
|
|||
case LJJHI_JWK: /* Optional: jwk JSON object: public key: */
|
||||
|
||||
jwk = n == LJJHI_EPK ? &jose->recipient[0].jwk_ephemeral : aux_jwk;
|
||||
if (!jwk)
|
||||
return -1;
|
||||
if (!jwk || !jwk->kty)
|
||||
break;
|
||||
|
||||
out += lws_snprintf(out, end - out, "%c\"%s\":",
|
||||
sub ? ',' : ' ', jws_jose[n]);
|
||||
out += lws_snprintf(out, end - out, "%s\"%s\":",
|
||||
sub ? ",\n" : "", jws_jose[n]);
|
||||
sub = 1;
|
||||
vl = end - out;
|
||||
m = lws_jwk_export(jwk, 0, out, &vl);
|
||||
|
@ -559,12 +559,12 @@ lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk,
|
|||
break;
|
||||
|
||||
out += lws_snprintf(out, end - out,
|
||||
"%c\"%s\":[", sub ? ',' : ' ', jws_jose[n]);
|
||||
"%s\"%s\":[", sub ? ",\n" : "", jws_jose[n]);
|
||||
sub = 1;
|
||||
|
||||
m = 0;
|
||||
f = 1;
|
||||
while (m < jose->e[n].len && (end - out) > 1) {
|
||||
while ((unsigned int)m < jose->e[n].len && (end - out) > 1) {
|
||||
if (jose->e[n].buf[m] == ' ') {
|
||||
if (!f)
|
||||
*out++ = '\"';
|
||||
|
|
|
@ -21,13 +21,20 @@
|
|||
|
||||
#include "core/private.h"
|
||||
#include "private.h"
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* Currently only support flattened or compact (implicitly single signature)
|
||||
*/
|
||||
|
||||
static const char * const jws_json[] = {
|
||||
"protected",
|
||||
"header",
|
||||
"payload",
|
||||
"signature",
|
||||
"signatures",
|
||||
"protected", /* base64u */
|
||||
"header", /* JSON */
|
||||
"payload", /* base64u payload */
|
||||
"signature", /* base64u signature */
|
||||
|
||||
//"signatures[].protected",
|
||||
//"signatures[].header",
|
||||
//"signatures[].signature"
|
||||
};
|
||||
|
||||
enum lws_jws_json_tok {
|
||||
|
@ -35,18 +42,121 @@ enum lws_jws_json_tok {
|
|||
LJWSJT_HEADER,
|
||||
LJWSJT_PAYLOAD,
|
||||
LJWSJT_SIGNATURE,
|
||||
LJWSJT_SIGNATURES,
|
||||
|
||||
// LJWSJT_SIGNATURES_PROTECTED,
|
||||
// LJWSJT_SIGNATURES_HEADER,
|
||||
// LJWSJT_SIGNATURES_SIGNATURE,
|
||||
};
|
||||
|
||||
/* parse a JWS complete or flattened JSON object */
|
||||
|
||||
struct jws_cb_args {
|
||||
struct lws_jws *jws;
|
||||
|
||||
char *temp;
|
||||
int *temp_len;
|
||||
};
|
||||
|
||||
static signed char
|
||||
lws_jws_json_cb(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct jose_cb_args *args = (struct jose_cb_args *)ctx->user;
|
||||
int n;
|
||||
struct jws_cb_args *args = (struct jws_cb_args *)ctx->user;
|
||||
int n, m;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
|
||||
/* strings */
|
||||
|
||||
case LJWSJT_PROTECTED: /* base64u: JOSE: must contain 'alg' */
|
||||
m = LJWS_JOSE;
|
||||
goto append_string;
|
||||
case LJWSJT_PAYLOAD: /* base64u */
|
||||
m = LJWS_PYLD;
|
||||
goto append_string;
|
||||
case LJWSJT_SIGNATURE: /* base64u */
|
||||
m = LJWS_SIG;
|
||||
goto append_string;
|
||||
|
||||
case LJWSJT_HEADER: /* unprotected freeform JSON */
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
append_string:
|
||||
|
||||
if (*args->temp_len < ctx->npos) {
|
||||
lwsl_err("%s: out of parsing space\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We keep both b64u and decoded in temp mapped using map / map_b64,
|
||||
* the jws signature is actually over the b64 content not the plaintext,
|
||||
* and we can't do it until we see the protected alg.
|
||||
*/
|
||||
|
||||
if (!args->jws->map_b64.buf[m]) {
|
||||
args->jws->map_b64.buf[m] = args->temp;
|
||||
args->jws->map_b64.len[m] = 0;
|
||||
}
|
||||
|
||||
memcpy(args->temp, ctx->buf, ctx->npos);
|
||||
args->temp += ctx->npos;
|
||||
*args->temp_len -= ctx->npos;
|
||||
args->jws->map_b64.len[m] += ctx->npos;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_END) {
|
||||
args->jws->map.buf[m] = args->temp;
|
||||
|
||||
n = lws_b64_decode_string_len(
|
||||
(const char *)args->jws->map_b64.buf[m],
|
||||
args->jws->map_b64.len[m],
|
||||
(char *)args->temp, *args->temp_len);
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: b64 decode failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
args->temp += n;
|
||||
*args->temp_len -= n;
|
||||
args->jws->map.len[m] = n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
lws_jws_json_parse(struct lws_jws *jws, const uint8_t *buf, int len,
|
||||
char *temp, int *temp_len)
|
||||
{
|
||||
struct jws_cb_args args;
|
||||
struct lejp_ctx jctx;
|
||||
int m = 0;
|
||||
|
||||
args.jws = jws;
|
||||
args.temp = temp;
|
||||
args.temp_len = temp_len;
|
||||
|
||||
lejp_construct(&jctx, lws_jws_json_cb, &args, jws_json,
|
||||
LWS_ARRAY_SIZE(jws_json));
|
||||
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, len);
|
||||
lejp_destruct(&jctx);
|
||||
if (m < 0) {
|
||||
lwsl_notice("%s: parse returned %d\n", __func__, m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk,
|
||||
struct lws_context *context)
|
||||
|
@ -608,8 +718,22 @@ lws_jws_sig_confirm_compact(struct lws_jws_map *map, struct lws_jwk *jwk,
|
|||
return lws_jws_sig_confirm(&map_b64, map, jwk, context);
|
||||
}
|
||||
|
||||
int
|
||||
lws_jws_sig_confirm_json(const char *in, size_t len,
|
||||
struct lws_jws *jws, struct lws_jwk *jwk,
|
||||
struct lws_context *context,
|
||||
char *temp, int *temp_len)
|
||||
{
|
||||
if (lws_jws_json_parse(jws, (const uint8_t *)in, len, temp, temp_len)) {
|
||||
lwsl_err("%s: lws_jws_json_parse failed\n", __func__);
|
||||
|
||||
LWS_VISIBLE int
|
||||
return -1;
|
||||
}
|
||||
return lws_jws_sig_confirm(&jws->map_b64, &jws->map, jwk, context);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws,
|
||||
char *b64_sig, size_t sig_len)
|
||||
{
|
||||
|
@ -768,18 +892,20 @@ lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len)
|
|||
if (len < 1)
|
||||
return 1;
|
||||
|
||||
n += lws_snprintf(flattened + n, len - n , "{\"payload\": \"%s\",\n",
|
||||
n += lws_snprintf(flattened + n, len - n , "{\"payload\": \"%.*s\",\n",
|
||||
jws->map_b64.len[LJWS_PYLD],
|
||||
jws->map_b64.buf[LJWS_PYLD]);
|
||||
|
||||
n += lws_snprintf(flattened + n, len - n , " \"protected\": \"%s\",\n",
|
||||
jws->map_b64.buf[LJWS_JOSE]);
|
||||
n += lws_snprintf(flattened + n, len - n , " \"protected\": \"%.*s\",\n",
|
||||
jws->map_b64.len[LJWS_JOSE],
|
||||
jws->map_b64.buf[LJWS_JOSE]);
|
||||
|
||||
if (jws->map_b64.buf[LJWS_UHDR])
|
||||
n += lws_snprintf(flattened + n, len - n , " \"header\": %s,\n",
|
||||
jws->map_b64.buf[LJWS_UHDR]);
|
||||
n += lws_snprintf(flattened + n, len - n , " \"header\": %.*s,\n",
|
||||
jws->map_b64.len[LJWS_UHDR], jws->map_b64.buf[LJWS_UHDR]);
|
||||
|
||||
n += lws_snprintf(flattened + n, len - n , " \"signature\": \"%s\"}\n",
|
||||
jws->map_b64.buf[LJWS_SIG]);
|
||||
n += lws_snprintf(flattened + n, len - n , " \"signature\": \"%.*s\"}\n",
|
||||
jws->map_b64.len[LJWS_SIG], jws->map_b64.buf[LJWS_SIG]);
|
||||
|
||||
return (n >= len - 1);
|
||||
}
|
||||
|
|
|
@ -563,6 +563,7 @@ static const struct lws_jose_jwe_alg lws_gencrypto_jwe_enc_map[] = {
|
|||
LWS_JOSE_ENCTYPE_AES_GCM,
|
||||
"A256GCM", NULL, 256, 256, 96
|
||||
},
|
||||
{ 0, 0, 0, 0, NULL, NULL, 0, 0, 0 } /* sentinel */
|
||||
};
|
||||
|
||||
LWS_VISIBLE int
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - generic EC api hiding the backend - common parts
|
||||
*
|
||||
* Copyright (C) 2017 - 2018 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2017 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -40,6 +40,61 @@ lws_genec_curve(const struct lws_ec_curves *table, const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
extern const struct lws_ec_curves lws_ec_curves[];
|
||||
|
||||
int
|
||||
lws_genec_confirm_curve_allowed_by_tls_id(const char *allowed, int id,
|
||||
struct lws_jwk *jwk)
|
||||
{
|
||||
struct lws_tokenize ts;
|
||||
lws_tokenize_elem e;
|
||||
int n, len;
|
||||
|
||||
lws_tokenize_init(&ts, allowed, LWS_TOKENIZE_F_COMMA_SEP_LIST |
|
||||
LWS_TOKENIZE_F_MINUS_NONTERM);
|
||||
ts.len = strlen(allowed);
|
||||
do {
|
||||
e = lws_tokenize(&ts);
|
||||
switch (e) {
|
||||
case LWS_TOKZE_TOKEN:
|
||||
n = 0;
|
||||
while (lws_ec_curves[n].name) {
|
||||
if (id != lws_ec_curves[n].tls_lib_nid) {
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
lwsl_info("match curve %s\n",
|
||||
lws_ec_curves[n].name);
|
||||
len = strlen(lws_ec_curves[n].name);
|
||||
jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len = len;
|
||||
jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf =
|
||||
lws_malloc(len + 1, "cert crv");
|
||||
if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) {
|
||||
lwsl_err("%s: OOM\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
memcpy(jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
|
||||
lws_ec_curves[n].name, len + 1);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_TOKZE_DELIMITER:
|
||||
break;
|
||||
|
||||
default: /* includes ENDED */
|
||||
lwsl_err("%s: malformed or curve name in list\n",
|
||||
__func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
} while (e > 0);
|
||||
|
||||
lwsl_err("%s: unsupported curve group nid %d\n", __func__, n);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_genec_destroy_elements(struct lws_gencrypto_keyelem *el)
|
||||
{
|
||||
|
|
|
@ -374,8 +374,8 @@ bail1:
|
|||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
|
||||
enum lws_genhash_types hash_type, int keybits,
|
||||
uint8_t *sig, size_t sig_len)
|
||||
enum lws_genhash_types hash_type, int keybits,
|
||||
uint8_t *sig, size_t sig_len)
|
||||
{
|
||||
int n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
|
||||
size_t hlen = lws_genhash_size(hash_type);
|
||||
|
@ -515,126 +515,3 @@ lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
LWS_VISIBLE int
|
||||
lws_genec_public_decrypt(struct lws_genec_ctx *ctx, const uint8_t *in,
|
||||
size_t in_len, uint8_t *out, size_t out_max)
|
||||
{
|
||||
size_t olen = 0;
|
||||
int n;
|
||||
|
||||
ctx->ctx->len = in_len;
|
||||
n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, NULL, NULL,
|
||||
MBEDTLS_RSA_PUBLIC,
|
||||
&olen, in, out, out_max);
|
||||
if (n) {
|
||||
lwsl_notice("%s: -0x%x\n", __func__, -n);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return olen;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_genec_public_encrypt(struct lws_genec_ctx *ctx, const uint8_t *in,
|
||||
size_t in_len, uint8_t *out)
|
||||
{
|
||||
int n;
|
||||
|
||||
//ctx->ctx->len = in_len; // ???
|
||||
ctx->ctx->padding = MBEDTLS_RSA_PKCS_V15;
|
||||
n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, _rngf, ctx->context,
|
||||
MBEDTLS_RSA_PRIVATE,
|
||||
in_len, in, out);
|
||||
if (n) {
|
||||
lwsl_notice("%s: -0x%x: in_len: %d\n", __func__, -n,
|
||||
(int)in_len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_genec_render_pkey_asn1(struct lws_genec_ctx *ctx, int _private,
|
||||
uint8_t *pkey_asn1, size_t pkey_asn1_len)
|
||||
{
|
||||
uint8_t *p = pkey_asn1, *totlen, *end = pkey_asn1 + pkey_asn1_len - 1;
|
||||
mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT] = {
|
||||
&ctx->ctx->N, &ctx->ctx->E, &ctx->ctx->D, &ctx->ctx->P,
|
||||
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
|
||||
&ctx->ctx->QP,
|
||||
};
|
||||
int n;
|
||||
|
||||
/* 30 82 - sequence
|
||||
* 09 29 <-- length(0x0929) less 4 bytes
|
||||
* 02 01 <- length (1)
|
||||
* 00
|
||||
* 02 82
|
||||
* 02 01 <- length (513) N
|
||||
* ...
|
||||
*
|
||||
* 02 03 <- length (3) E
|
||||
* 01 00 01
|
||||
*
|
||||
* 02 82
|
||||
* 02 00 <- length (512) D P Q EXP1 EXP2 COEFF
|
||||
*
|
||||
* */
|
||||
|
||||
*p++ = 0x30;
|
||||
*p++ = 0x82;
|
||||
totlen = p;
|
||||
p += 2;
|
||||
|
||||
*p++ = 0x02;
|
||||
*p++ = 0x01;
|
||||
*p++ = 0x00;
|
||||
|
||||
for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++) {
|
||||
int m = mbedtls_mpi_size(mpi[n]);
|
||||
uint8_t *elen;
|
||||
|
||||
*p++ = 0x02;
|
||||
elen = p;
|
||||
if (m < 0x7f)
|
||||
*p++ = m;
|
||||
else {
|
||||
*p++ = 0x82;
|
||||
*p++ = m >> 8;
|
||||
*p++ = m & 0xff;
|
||||
}
|
||||
|
||||
if (p + m > end)
|
||||
return -1;
|
||||
|
||||
mbedtls_mpi_write_binary(mpi[n], p, m);
|
||||
if (p[0] & 0x80) {
|
||||
p[0] = 0x00;
|
||||
mbedtls_mpi_write_binary(mpi[n], &p[1], m);
|
||||
m++;
|
||||
}
|
||||
if (m < 0x7f)
|
||||
*elen = m;
|
||||
else {
|
||||
*elen++ = 0x82;
|
||||
*elen++ = m >> 8;
|
||||
*elen = m & 0xff;
|
||||
}
|
||||
p += m;
|
||||
}
|
||||
|
||||
n = lws_ptr_diff(p, pkey_asn1);
|
||||
|
||||
*totlen++ = (n - 4) >> 8;
|
||||
*totlen = (n - 4) & 0xff;
|
||||
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
* gencrypto mbedtls-specific helper declarations
|
||||
*/
|
||||
|
||||
#include <mbedtls/x509_crl.h>
|
||||
|
||||
struct lws_x509_cert {
|
||||
mbedtls_x509_crt cert; /* has a .next for linked-list / chain */
|
||||
};
|
||||
|
||||
mbedtls_md_type_t
|
||||
lws_gencrypto_mbedtls_hash_to_MD_TYPE(enum lws_genhash_types hash_type);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - mbedTLS-specific lws apis
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
#include <mbedtls/oid.h>
|
||||
#include "tls/mbedtls/private.h"
|
||||
|
||||
void
|
||||
lws_tls_err_describe(void)
|
||||
|
@ -321,168 +321,6 @@ __lws_tls_shutdown(struct lws *wsi)
|
|||
}
|
||||
}
|
||||
|
||||
static time_t
|
||||
lws_tls_mbedtls_time_to_unix(mbedtls_x509_time *xtime)
|
||||
{
|
||||
struct tm t;
|
||||
|
||||
if (!xtime || !xtime->year || xtime->year < 0)
|
||||
return (time_t)(long long)-1;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tm_year = xtime->year - 1900;
|
||||
t.tm_mon = xtime->mon - 1; /* mbedtls months are 1+, tm are 0+ */
|
||||
t.tm_mday = xtime->day - 1; /* mbedtls days are 1+, tm are 0+ */
|
||||
t.tm_hour = xtime->hour;
|
||||
t.tm_min = xtime->min;
|
||||
t.tm_sec = xtime->sec;
|
||||
t.tm_isdst = -1;
|
||||
|
||||
return mktime(&t);
|
||||
}
|
||||
|
||||
static int
|
||||
lws_tls_mbedtls_get_x509_name(mbedtls_x509_name *name,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
while (name) {
|
||||
if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) {
|
||||
name = name->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len - 1 < name->val.len)
|
||||
return -1;
|
||||
|
||||
memcpy(&buf->ns.name[0], name->val.p, name->val.len);
|
||||
buf->ns.name[name->val.len] = '\0';
|
||||
buf->ns.len = name->val.len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
if (!x509)
|
||||
return -1;
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_FROM:
|
||||
buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_from);
|
||||
if (buf->time == (time_t)(long long)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_TO:
|
||||
buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_to);
|
||||
if (buf->time == (time_t)(long long)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_COMMON_NAME:
|
||||
return lws_tls_mbedtls_get_x509_name(&x509->subject, buf, len);
|
||||
|
||||
case LWS_TLS_CERT_INFO_ISSUER_NAME:
|
||||
return lws_tls_mbedtls_get_x509_name(&x509->issuer, buf, len);
|
||||
|
||||
case LWS_TLS_CERT_INFO_USAGE:
|
||||
buf->usage = x509->key_usage;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
|
||||
{
|
||||
char *p = buf->ns.name;
|
||||
size_t r = len, u;
|
||||
|
||||
switch (mbedtls_pk_get_type(&x509->pk)) {
|
||||
case MBEDTLS_PK_RSA:
|
||||
{
|
||||
mbedtls_rsa_context *rsa = mbedtls_pk_rsa(x509->pk);
|
||||
|
||||
if (mbedtls_mpi_write_string(&rsa->N, 16, p, r, &u))
|
||||
return -1;
|
||||
r -= u;
|
||||
p += u;
|
||||
if (mbedtls_mpi_write_string(&rsa->E, 16, p, r, &u))
|
||||
return -1;
|
||||
|
||||
p += u;
|
||||
buf->ns.len = lws_ptr_diff(p, buf->ns.name);
|
||||
break;
|
||||
}
|
||||
case MBEDTLS_PK_ECKEY:
|
||||
{
|
||||
mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(x509->pk);
|
||||
|
||||
if (mbedtls_mpi_write_string(&ecp->Q.X, 16, p, r, &u))
|
||||
return -1;
|
||||
r -= u;
|
||||
p += u;
|
||||
if (mbedtls_mpi_write_string(&ecp->Q.Y, 16, p, r, &u))
|
||||
return -1;
|
||||
r -= u;
|
||||
p += u;
|
||||
if (mbedtls_mpi_write_string(&ecp->Q.Z, 16, p, r, &u))
|
||||
return -1;
|
||||
p += u;
|
||||
buf->ns.len = lws_ptr_diff(p, buf->ns.name);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
lwsl_notice("%s: x509 has unsupported pubkey type %d\n",
|
||||
__func__,
|
||||
mbedtls_pk_get_type(&x509->pk));
|
||||
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
mbedtls_x509_crt *x509 = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx);
|
||||
|
||||
return lws_tls_mbedtls_cert_info(x509, type, buf, len);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
mbedtls_x509_crt *x509;
|
||||
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl);
|
||||
|
||||
if (!x509)
|
||||
return -1;
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VERIFIED:
|
||||
buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK;
|
||||
return 0;
|
||||
default:
|
||||
return lws_tls_mbedtls_cert_info(x509, type, buf, len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
tops_fake_POLLIN_for_buffered_mbedtls(struct lws_context_per_thread *pt)
|
||||
|
|
411
lib/tls/mbedtls/x509.c
Normal file
411
lib/tls/mbedtls/x509.c
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* libwebsockets - mbedTLS-specific lws apis
|
||||
*
|
||||
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
#include "tls/mbedtls/private.h"
|
||||
#include <mbedtls/oid.h>
|
||||
|
||||
static time_t
|
||||
lws_tls_mbedtls_time_to_unix(mbedtls_x509_time *xtime)
|
||||
{
|
||||
struct tm t;
|
||||
|
||||
if (!xtime || !xtime->year || xtime->year < 0)
|
||||
return (time_t)(long long)-1;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tm_year = xtime->year - 1900;
|
||||
t.tm_mon = xtime->mon - 1; /* mbedtls months are 1+, tm are 0+ */
|
||||
t.tm_mday = xtime->day - 1; /* mbedtls days are 1+, tm are 0+ */
|
||||
t.tm_hour = xtime->hour;
|
||||
t.tm_min = xtime->min;
|
||||
t.tm_sec = xtime->sec;
|
||||
t.tm_isdst = -1;
|
||||
|
||||
return mktime(&t);
|
||||
}
|
||||
|
||||
static int
|
||||
lws_tls_mbedtls_get_x509_name(mbedtls_x509_name *name,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
while (name) {
|
||||
if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) {
|
||||
name = name->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len - 1 < name->val.len)
|
||||
return -1;
|
||||
|
||||
memcpy(&buf->ns.name[0], name->val.p, name->val.len);
|
||||
buf->ns.name[name->val.len] = '\0';
|
||||
buf->ns.len = name->val.len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_tls_mbedtls_cert_info(mbedtls_x509_crt *x509, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
if (!x509)
|
||||
return -1;
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_FROM:
|
||||
buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_from);
|
||||
if (buf->time == (time_t)(long long)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_TO:
|
||||
buf->time = lws_tls_mbedtls_time_to_unix(&x509->valid_to);
|
||||
if (buf->time == (time_t)(long long)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_COMMON_NAME:
|
||||
return lws_tls_mbedtls_get_x509_name(&x509->subject, buf, len);
|
||||
|
||||
case LWS_TLS_CERT_INFO_ISSUER_NAME:
|
||||
return lws_tls_mbedtls_get_x509_name(&x509->issuer, buf, len);
|
||||
|
||||
case LWS_TLS_CERT_INFO_USAGE:
|
||||
buf->usage = x509->key_usage;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
|
||||
{
|
||||
char *p = buf->ns.name;
|
||||
size_t r = len, u;
|
||||
|
||||
switch (mbedtls_pk_get_type(&x509->pk)) {
|
||||
case MBEDTLS_PK_RSA:
|
||||
{
|
||||
mbedtls_rsa_context *rsa = mbedtls_pk_rsa(x509->pk);
|
||||
|
||||
if (mbedtls_mpi_write_string(&rsa->N, 16, p, r, &u))
|
||||
return -1;
|
||||
r -= u;
|
||||
p += u;
|
||||
if (mbedtls_mpi_write_string(&rsa->E, 16, p, r, &u))
|
||||
return -1;
|
||||
|
||||
p += u;
|
||||
buf->ns.len = lws_ptr_diff(p, buf->ns.name);
|
||||
break;
|
||||
}
|
||||
case MBEDTLS_PK_ECKEY:
|
||||
{
|
||||
mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(x509->pk);
|
||||
|
||||
if (mbedtls_mpi_write_string(&ecp->Q.X, 16, p, r, &u))
|
||||
return -1;
|
||||
r -= u;
|
||||
p += u;
|
||||
if (mbedtls_mpi_write_string(&ecp->Q.Y, 16, p, r, &u))
|
||||
return -1;
|
||||
r -= u;
|
||||
p += u;
|
||||
if (mbedtls_mpi_write_string(&ecp->Q.Z, 16, p, r, &u))
|
||||
return -1;
|
||||
p += u;
|
||||
buf->ns.len = lws_ptr_diff(p, buf->ns.name);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
lwsl_notice("%s: x509 has unsupported pubkey type %d\n",
|
||||
__func__,
|
||||
mbedtls_pk_get_type(&x509->pk));
|
||||
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
mbedtls_x509_crt *x509;
|
||||
|
||||
x509 = ssl_ctx_get_mbedtls_x509_crt(vhost->tls.ssl_ctx);
|
||||
|
||||
return lws_tls_mbedtls_cert_info(x509, type, buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
mbedtls_x509_crt *x509;
|
||||
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
x509 = ssl_get_peer_mbedtls_x509_crt(wsi->tls.ssl);
|
||||
|
||||
if (!x509)
|
||||
return -1;
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VERIFIED:
|
||||
buf->verified = SSL_get_verify_result(wsi->tls.ssl) == X509_V_OK;
|
||||
return 0;
|
||||
default:
|
||||
return lws_tls_mbedtls_cert_info(x509, type, buf, len);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
return lws_tls_mbedtls_cert_info(&x509->cert, type, buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_create(struct lws_x509_cert **x509)
|
||||
{
|
||||
*x509 = lws_malloc(sizeof(**x509), __func__);
|
||||
|
||||
return !(*x509);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse one DER-encoded or one or more concatenated PEM-encoded certificates
|
||||
* and add them to the chained list.
|
||||
*/
|
||||
|
||||
int
|
||||
lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mbedtls_x509_crt_init(&x509->cert);
|
||||
|
||||
ret = mbedtls_x509_crt_parse(&x509->cert, pem, len);
|
||||
if (ret) {
|
||||
mbedtls_x509_crt_free(&x509->cert);
|
||||
lwsl_err("%s: unable to parse PEM cert: -0x%x\n",
|
||||
__func__, -ret);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted,
|
||||
const char *common_name)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
int ret;
|
||||
|
||||
ret = mbedtls_x509_crt_verify_with_profile(&x509->cert, &trusted->cert,
|
||||
NULL,
|
||||
&mbedtls_x509_crt_profile_next,
|
||||
common_name, &flags, NULL,
|
||||
NULL);
|
||||
|
||||
if (ret) {
|
||||
lwsl_err("%s: unable to parse PEM cert: -0x%x\n",
|
||||
__func__, -ret);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_JOSE)
|
||||
|
||||
int
|
||||
lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509,
|
||||
const char *curves, int rsa_min_bits)
|
||||
{
|
||||
int kt = mbedtls_pk_get_type(&x509->cert.pk), n, count = 0, ret = -1;
|
||||
mbedtls_rsa_context *rsactx;
|
||||
mbedtls_ecp_keypair *ecpctx;
|
||||
mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT];
|
||||
|
||||
memset(jwk, 0, sizeof(*jwk));
|
||||
|
||||
switch (kt) {
|
||||
case MBEDTLS_PK_RSA:
|
||||
lwsl_notice("%s: RSA key\n", __func__);
|
||||
jwk->kty = LWS_GENCRYPTO_KTY_RSA;
|
||||
rsactx = mbedtls_pk_rsa(x509->cert.pk);
|
||||
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = &rsactx->E;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = &rsactx->N;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = &rsactx->D;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_P] = &rsactx->P;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_Q] = &rsactx->Q;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_DP] = &rsactx->DP;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_DQ] = &rsactx->DQ;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_QI] = &rsactx->QP;
|
||||
|
||||
count = LWS_GENCRYPTO_RSA_KEYEL_COUNT;
|
||||
n = LWS_GENCRYPTO_RSA_KEYEL_E;
|
||||
break;
|
||||
|
||||
case MBEDTLS_PK_ECKEY:
|
||||
lwsl_notice("%s: EC key\n", __func__);
|
||||
jwk->kty = LWS_GENCRYPTO_KTY_EC;
|
||||
ecpctx = mbedtls_pk_ec(x509->cert.pk);
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_X] = &ecpctx->Q.X;
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_D] = &ecpctx->d;
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = &ecpctx->Q.Y;
|
||||
|
||||
if (lws_genec_confirm_curve_allowed_by_tls_id(curves,
|
||||
ecpctx->grp.id, jwk))
|
||||
/* already logged */
|
||||
goto bail;
|
||||
|
||||
count = LWS_GENCRYPTO_EC_KEYEL_COUNT;
|
||||
n = LWS_GENCRYPTO_EC_KEYEL_X;
|
||||
break;
|
||||
default:
|
||||
lwsl_err("%s: key type %d not supported\n", __func__, kt);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (; n < count; n++) {
|
||||
if (!mbedtls_mpi_size(mpi[n]))
|
||||
continue;
|
||||
|
||||
jwk->e[n].buf = lws_malloc(mbedtls_mpi_size(mpi[n]), "certjwk");
|
||||
if (!jwk->e[n].buf)
|
||||
goto bail;
|
||||
jwk->e[n].len = mbedtls_mpi_size(mpi[n]);
|
||||
mbedtls_mpi_write_binary(mpi[n], jwk->e[n].buf, jwk->e[n].len);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
/* jwk destroy will clean up partials */
|
||||
if (ret)
|
||||
lws_jwk_destroy(jwk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len,
|
||||
const char *passphrase)
|
||||
{
|
||||
mbedtls_rsa_context *rsactx;
|
||||
mbedtls_ecp_keypair *ecpctx;
|
||||
mbedtls_pk_context pk;
|
||||
mbedtls_mpi *mpi[LWS_GENCRYPTO_RSA_KEYEL_COUNT];
|
||||
int n, ret = -1, count = 0;
|
||||
|
||||
mbedtls_pk_init(&pk);
|
||||
|
||||
n = 0;
|
||||
if (passphrase)
|
||||
n = strlen(passphrase);
|
||||
n = mbedtls_pk_parse_key(&pk, pem, len, (uint8_t *)passphrase, n);
|
||||
if (n) {
|
||||
lwsl_err("%s: parse PEM key failed: -0x%x\n", __func__, -n);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* the incoming private key type */
|
||||
switch (mbedtls_pk_get_type(&pk)) {
|
||||
case MBEDTLS_PK_RSA:
|
||||
if (jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
|
||||
lwsl_err("%s: RSA privkey, non-RSA jwk\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
rsactx = mbedtls_pk_rsa(pk);
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = &rsactx->D;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_P] = &rsactx->P;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_Q] = &rsactx->Q;
|
||||
n = LWS_GENCRYPTO_RSA_KEYEL_D;
|
||||
count = LWS_GENCRYPTO_RSA_KEYEL_Q + 1;
|
||||
break;
|
||||
case MBEDTLS_PK_ECKEY:
|
||||
if (jwk->kty != LWS_GENCRYPTO_KTY_EC) {
|
||||
lwsl_err("%s: EC privkey, non-EC jwk\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
ecpctx = mbedtls_pk_ec(pk);
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_D] = &ecpctx->d;
|
||||
n = LWS_GENCRYPTO_EC_KEYEL_D;
|
||||
count = n + 1;
|
||||
break;
|
||||
default:
|
||||
lwsl_err("%s: unusable key type %d\n", __func__,
|
||||
mbedtls_pk_get_type(&pk));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (; n < count; n++) {
|
||||
if (!mbedtls_mpi_size(mpi[n])) {
|
||||
lwsl_err("%s: empty privkey\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
jwk->e[n].buf = lws_malloc(mbedtls_mpi_size(mpi[n]), "certjwk");
|
||||
if (!jwk->e[n].buf)
|
||||
goto bail;
|
||||
jwk->e[n].len = mbedtls_mpi_size(mpi[n]);
|
||||
mbedtls_mpi_write_binary(mpi[n], jwk->e[n].buf, jwk->e[n].len);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
mbedtls_pk_free(&pk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
lws_x509_destroy(struct lws_x509_cert **x509)
|
||||
{
|
||||
if (!*x509)
|
||||
return;
|
||||
|
||||
mbedtls_x509_crt_free(&(*x509)->cert);
|
||||
|
||||
lws_free_set_NULL(*x509);
|
||||
}
|
|
@ -53,7 +53,7 @@ ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
|||
}
|
||||
#endif
|
||||
#if !defined(LWS_HAVE_BN_bn2binpad)
|
||||
static int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
|
||||
int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
|
||||
{
|
||||
int i;
|
||||
BN_ULONG l;
|
||||
|
@ -377,7 +377,7 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
|
|||
goto bail2;
|
||||
|
||||
m = BN_bn2binpad(bn[n - 1], el[n].buf, el[n].len);
|
||||
if (m != el[n].len)
|
||||
if ((uint32_t)m != el[n].len)
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -21,8 +21,16 @@
|
|||
* gencrypto openssl-specific helper declarations
|
||||
*/
|
||||
|
||||
struct lws_x509_cert {
|
||||
X509 *cert; /* X509 is opaque, this has to be a pointer */
|
||||
};
|
||||
|
||||
int
|
||||
lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type);
|
||||
|
||||
const EVP_MD *
|
||||
lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type);
|
||||
|
||||
#if !defined(LWS_HAVE_BN_bn2binpad)
|
||||
int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
|
||||
#endif
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
#include "tls/openssl/private.h"
|
||||
#include <errno.h>
|
||||
|
||||
int openssl_websocket_private_data_index,
|
||||
|
@ -550,196 +551,7 @@ __lws_tls_shutdown(struct lws *wsi)
|
|||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
}
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
static int
|
||||
dec(char c)
|
||||
{
|
||||
return c - '0';
|
||||
}
|
||||
#endif
|
||||
|
||||
static time_t
|
||||
lws_tls_openssl_asn1time_to_unix(ASN1_TIME *as)
|
||||
{
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
|
||||
const char *p = (const char *)as->data;
|
||||
struct tm t;
|
||||
|
||||
/* [YY]YYMMDDHHMMSSZ */
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
if (strlen(p) == 13) {
|
||||
t.tm_year = (dec(p[0]) * 10) + dec(p[1]) + 100;
|
||||
p += 2;
|
||||
} else {
|
||||
t.tm_year = (dec(p[0]) * 1000) + (dec(p[1]) * 100) +
|
||||
(dec(p[2]) * 10) + dec(p[3]);
|
||||
p += 4;
|
||||
}
|
||||
t.tm_mon = (dec(p[0]) * 10) + dec(p[1]) - 1;
|
||||
p += 2;
|
||||
t.tm_mday = (dec(p[0]) * 10) + dec(p[1]) - 1;
|
||||
p += 2;
|
||||
t.tm_hour = (dec(p[0]) * 10) + dec(p[1]);
|
||||
p += 2;
|
||||
t.tm_min = (dec(p[0]) * 10) + dec(p[1]);
|
||||
p += 2;
|
||||
t.tm_sec = (dec(p[0]) * 10) + dec(p[1]);
|
||||
t.tm_isdst = 0;
|
||||
|
||||
return mktime(&t);
|
||||
#else
|
||||
return (time_t)-1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
X509_NAME *xn;
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
char *p;
|
||||
#endif
|
||||
|
||||
if (!x509)
|
||||
return -1;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(X509_get_notBefore)
|
||||
#define X509_get_notBefore(x) X509_getm_notBefore(x)
|
||||
#define X509_get_notAfter(x) X509_getm_notAfter(x)
|
||||
#endif
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_FROM:
|
||||
buf->time = lws_tls_openssl_asn1time_to_unix(
|
||||
X509_get_notBefore(x509));
|
||||
if (buf->time == (time_t)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_TO:
|
||||
buf->time = lws_tls_openssl_asn1time_to_unix(
|
||||
X509_get_notAfter(x509));
|
||||
if (buf->time == (time_t)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_COMMON_NAME:
|
||||
#if defined(LWS_PLAT_OPTEE)
|
||||
return -1;
|
||||
#else
|
||||
xn = X509_get_subject_name(x509);
|
||||
if (!xn)
|
||||
return -1;
|
||||
X509_NAME_oneline(xn, buf->ns.name, (int)len - 2);
|
||||
p = strstr(buf->ns.name, "/CN=");
|
||||
if (p)
|
||||
memmove(buf->ns.name, p + 4, strlen(p + 4) + 1);
|
||||
buf->ns.len = (int)strlen(buf->ns.name);
|
||||
return 0;
|
||||
#endif
|
||||
case LWS_TLS_CERT_INFO_ISSUER_NAME:
|
||||
xn = X509_get_issuer_name(x509);
|
||||
if (!xn)
|
||||
return -1;
|
||||
X509_NAME_oneline(xn, buf->ns.name, (int)len - 1);
|
||||
buf->ns.len = (int)strlen(buf->ns.name);
|
||||
return 0;
|
||||
|
||||
case LWS_TLS_CERT_INFO_USAGE:
|
||||
#if defined(LWS_HAVE_X509_get_key_usage)
|
||||
buf->usage = X509_get_key_usage(x509);
|
||||
break;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
|
||||
{
|
||||
#ifndef USE_WOLFSSL
|
||||
size_t klen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), NULL);
|
||||
uint8_t *tmp, *ptmp;
|
||||
|
||||
if (!klen || klen > len)
|
||||
return -1;
|
||||
|
||||
tmp = (uint8_t *)OPENSSL_malloc(klen);
|
||||
if (!tmp)
|
||||
return -1;
|
||||
|
||||
ptmp = tmp;
|
||||
if (i2d_X509_PUBKEY(
|
||||
X509_get_X509_PUBKEY(x509), &ptmp) != (int)klen ||
|
||||
!ptmp || lws_ptr_diff(ptmp, tmp) != (int)klen) {
|
||||
lwsl_info("%s: cert public key extraction failed\n",
|
||||
__func__);
|
||||
if (ptmp)
|
||||
OPENSSL_free(tmp);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf->ns.len = (int)klen;
|
||||
memcpy(buf->ns.name, tmp, klen);
|
||||
OPENSSL_free(tmp);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
#if defined(LWS_HAVE_SSL_CTX_get0_certificate)
|
||||
X509 *x509 = SSL_CTX_get0_certificate(vhost->tls.ssl_ctx);
|
||||
|
||||
return lws_tls_openssl_cert_info(x509, type, buf, len);
|
||||
#else
|
||||
lwsl_notice("openssl is too old to support %s\n", __func__);
|
||||
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
int rc = 0;
|
||||
X509 *x509;
|
||||
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
x509 = SSL_get_peer_certificate(wsi->tls.ssl);
|
||||
|
||||
if (!x509) {
|
||||
lwsl_debug("no peer cert\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VERIFIED:
|
||||
buf->verified = SSL_get_verify_result(wsi->tls.ssl) ==
|
||||
X509_V_OK;
|
||||
break;
|
||||
default:
|
||||
rc = lws_tls_openssl_cert_info(x509, type, buf, len);
|
||||
}
|
||||
|
||||
X509_free(x509);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
tops_fake_POLLIN_for_buffered_openssl(struct lws_context_per_thread *pt)
|
||||
|
|
633
lib/tls/openssl/x509.c
Normal file
633
lib/tls/openssl/x509.c
Normal file
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
* libwebsockets - mbedTLS-specific lws apis
|
||||
*
|
||||
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
#include "tls/openssl/private.h"
|
||||
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
static int
|
||||
dec(char c)
|
||||
{
|
||||
return c - '0';
|
||||
}
|
||||
#endif
|
||||
|
||||
static time_t
|
||||
lws_tls_openssl_asn1time_to_unix(ASN1_TIME *as)
|
||||
{
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
|
||||
const char *p = (const char *)as->data;
|
||||
struct tm t;
|
||||
|
||||
/* [YY]YYMMDDHHMMSSZ */
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
if (strlen(p) == 13) {
|
||||
t.tm_year = (dec(p[0]) * 10) + dec(p[1]) + 100;
|
||||
p += 2;
|
||||
} else {
|
||||
t.tm_year = (dec(p[0]) * 1000) + (dec(p[1]) * 100) +
|
||||
(dec(p[2]) * 10) + dec(p[3]);
|
||||
p += 4;
|
||||
}
|
||||
t.tm_mon = (dec(p[0]) * 10) + dec(p[1]) - 1;
|
||||
p += 2;
|
||||
t.tm_mday = (dec(p[0]) * 10) + dec(p[1]) - 1;
|
||||
p += 2;
|
||||
t.tm_hour = (dec(p[0]) * 10) + dec(p[1]);
|
||||
p += 2;
|
||||
t.tm_min = (dec(p[0]) * 10) + dec(p[1]);
|
||||
p += 2;
|
||||
t.tm_sec = (dec(p[0]) * 10) + dec(p[1]);
|
||||
t.tm_isdst = 0;
|
||||
|
||||
return mktime(&t);
|
||||
#else
|
||||
return (time_t)-1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
X509_NAME *xn;
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
char *p;
|
||||
#endif
|
||||
|
||||
if (!x509)
|
||||
return -1;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(X509_get_notBefore)
|
||||
#define X509_get_notBefore(x) X509_getm_notBefore(x)
|
||||
#define X509_get_notAfter(x) X509_getm_notAfter(x)
|
||||
#endif
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_FROM:
|
||||
buf->time = lws_tls_openssl_asn1time_to_unix(
|
||||
X509_get_notBefore(x509));
|
||||
if (buf->time == (time_t)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_VALIDITY_TO:
|
||||
buf->time = lws_tls_openssl_asn1time_to_unix(
|
||||
X509_get_notAfter(x509));
|
||||
if (buf->time == (time_t)-1)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_TLS_CERT_INFO_COMMON_NAME:
|
||||
#if defined(LWS_PLAT_OPTEE)
|
||||
return -1;
|
||||
#else
|
||||
xn = X509_get_subject_name(x509);
|
||||
if (!xn)
|
||||
return -1;
|
||||
X509_NAME_oneline(xn, buf->ns.name, (int)len - 2);
|
||||
p = strstr(buf->ns.name, "/CN=");
|
||||
if (p)
|
||||
memmove(buf->ns.name, p + 4, strlen(p + 4) + 1);
|
||||
buf->ns.len = (int)strlen(buf->ns.name);
|
||||
return 0;
|
||||
#endif
|
||||
case LWS_TLS_CERT_INFO_ISSUER_NAME:
|
||||
xn = X509_get_issuer_name(x509);
|
||||
if (!xn)
|
||||
return -1;
|
||||
X509_NAME_oneline(xn, buf->ns.name, (int)len - 1);
|
||||
buf->ns.len = (int)strlen(buf->ns.name);
|
||||
return 0;
|
||||
|
||||
case LWS_TLS_CERT_INFO_USAGE:
|
||||
#if defined(LWS_HAVE_X509_get_key_usage)
|
||||
buf->usage = X509_get_key_usage(x509);
|
||||
break;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
|
||||
{
|
||||
#ifndef USE_WOLFSSL
|
||||
size_t klen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), NULL);
|
||||
uint8_t *tmp, *ptmp;
|
||||
|
||||
if (!klen || klen > len)
|
||||
return -1;
|
||||
|
||||
tmp = (uint8_t *)OPENSSL_malloc(klen);
|
||||
if (!tmp)
|
||||
return -1;
|
||||
|
||||
ptmp = tmp;
|
||||
if (i2d_X509_PUBKEY(
|
||||
X509_get_X509_PUBKEY(x509), &ptmp) != (int)klen ||
|
||||
!ptmp || lws_ptr_diff(ptmp, tmp) != (int)klen) {
|
||||
lwsl_info("%s: cert public key extraction failed\n",
|
||||
__func__);
|
||||
if (ptmp)
|
||||
OPENSSL_free(tmp);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf->ns.len = (int)klen;
|
||||
memcpy(buf->ns.name, tmp, klen);
|
||||
OPENSSL_free(tmp);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
#if defined(LWS_HAVE_SSL_CTX_get0_certificate)
|
||||
X509 *x509 = SSL_CTX_get0_certificate(vhost->tls.ssl_ctx);
|
||||
|
||||
return lws_tls_openssl_cert_info(x509, type, buf, len);
|
||||
#else
|
||||
lwsl_notice("openssl is too old to support %s\n", __func__);
|
||||
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
return lws_tls_openssl_cert_info(x509->cert, type, buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
|
||||
union lws_tls_cert_info_results *buf, size_t len)
|
||||
{
|
||||
int rc = 0;
|
||||
X509 *x509;
|
||||
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
x509 = SSL_get_peer_certificate(wsi->tls.ssl);
|
||||
|
||||
if (!x509) {
|
||||
lwsl_debug("no peer cert\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case LWS_TLS_CERT_INFO_VERIFIED:
|
||||
buf->verified = SSL_get_verify_result(wsi->tls.ssl) ==
|
||||
X509_V_OK;
|
||||
break;
|
||||
default:
|
||||
rc = lws_tls_openssl_cert_info(x509, type, buf, len);
|
||||
}
|
||||
|
||||
X509_free(x509);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_create(struct lws_x509_cert **x509)
|
||||
{
|
||||
*x509 = lws_malloc(sizeof(**x509), __func__);
|
||||
if (*x509)
|
||||
(*x509)->cert = NULL;
|
||||
|
||||
return !(*x509);
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len)
|
||||
{
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
|
||||
BIO_write(bio, pem, len);
|
||||
x509->cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
if (!x509->cert) {
|
||||
lwsl_err("%s: unable to parse PEM cert\n", __func__);
|
||||
lws_tls_err_describe();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted,
|
||||
const char *common_name)
|
||||
{
|
||||
char c[32], *p;
|
||||
int ret;
|
||||
|
||||
if (common_name) {
|
||||
X509_NAME *xn = X509_get_subject_name(x509->cert);
|
||||
if (!xn)
|
||||
return -1;
|
||||
X509_NAME_oneline(xn, c, (int)sizeof(c) - 2);
|
||||
p = strstr(c, "/CN=");
|
||||
if (p)
|
||||
p = p + 4;
|
||||
else
|
||||
p = c;
|
||||
|
||||
if (strcmp(p, common_name)) {
|
||||
lwsl_err("%s: common name mismatch\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = X509_check_issued(trusted->cert, x509->cert);
|
||||
if (ret != X509_V_OK) {
|
||||
lwsl_err("%s: unable to verify cert relationship\n", __func__);
|
||||
lws_tls_err_describe();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_JOSE)
|
||||
int
|
||||
lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509,
|
||||
const char *curves, int rsa_min_bits)
|
||||
{
|
||||
int id, n, ret = -1, count;
|
||||
ASN1_OBJECT *obj = NULL;
|
||||
const EC_POINT *ecpoint;
|
||||
const EC_GROUP *ecgroup;
|
||||
X509_PUBKEY *pubkey;
|
||||
BIGNUM *mpi[4];
|
||||
EVP_PKEY *pkey;
|
||||
EC_KEY *ecpub;
|
||||
RSA *rsapub;
|
||||
|
||||
memset(jwk, 0, sizeof(*jwk));
|
||||
|
||||
pubkey = X509_get_X509_PUBKEY(x509->cert);
|
||||
if (!pubkey) {
|
||||
lwsl_err("%s: missing pubkey alg in cert\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (X509_PUBKEY_get0_param(&obj, NULL, NULL, NULL, pubkey) != 1) {
|
||||
lwsl_err("%s: missing pubkey alg in cert\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
id = OBJ_obj2nid(obj);
|
||||
if (id == NID_undef) {
|
||||
lwsl_err("%s: missing pubkey alg in cert\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: key type %d \"%s\"\n", __func__, id, OBJ_nid2ln(id));
|
||||
|
||||
pkey = X509_get_pubkey(x509->cert);
|
||||
if (!pkey) {
|
||||
lwsl_notice("%s: unable to extract pubkey", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case NID_X9_62_id_ecPublicKey:
|
||||
lwsl_debug("%s: EC key\n", __func__);
|
||||
jwk->kty = LWS_GENCRYPTO_KTY_EC;
|
||||
|
||||
if (!curves) {
|
||||
lwsl_err("%s: ec curves not allowed\n", __func__);
|
||||
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
ecpub = EVP_PKEY_get1_EC_KEY(pkey);
|
||||
if (!ecpub) {
|
||||
lwsl_notice("%s: missing EC pubkey\n", __func__);
|
||||
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
ecpoint = EC_KEY_get0_public_key(ecpub);
|
||||
if (!ecpoint) {
|
||||
lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
ecgroup = EC_KEY_get0_group(ecpub);
|
||||
if (!ecgroup) {
|
||||
lwsl_err("%s: EC_KEY_get0_group failed\n", __func__);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* validate the curve against ones we allow */
|
||||
|
||||
if (lws_genec_confirm_curve_allowed_by_tls_id(curves,
|
||||
EC_GROUP_get_curve_name(ecgroup), jwk))
|
||||
/* already logged */
|
||||
goto bail2;
|
||||
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_CRV] = NULL;
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_X] = BN_new(); /* X */
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_D] = NULL;
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = BN_new(); /* Y */
|
||||
|
||||
if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint,
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_X],
|
||||
mpi[LWS_GENCRYPTO_EC_KEYEL_Y],
|
||||
NULL) != 1) {
|
||||
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
|
||||
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
|
||||
lwsl_err("%s: EC_POINT_get_aff failed\n", __func__);
|
||||
goto bail2;
|
||||
}
|
||||
count = LWS_GENCRYPTO_EC_KEYEL_COUNT;
|
||||
n = LWS_GENCRYPTO_EC_KEYEL_X;
|
||||
break;
|
||||
|
||||
case NID_rsaEncryption:
|
||||
lwsl_debug("%s: rsa key\n", __func__);
|
||||
jwk->kty = LWS_GENCRYPTO_KTY_RSA;
|
||||
|
||||
rsapub = EVP_PKEY_get1_RSA(pkey);
|
||||
if (!rsapub) {
|
||||
lwsl_notice("%s: missing RSA pubkey\n", __func__);
|
||||
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
if (RSA_size(rsapub) * 8 < rsa_min_bits) {
|
||||
lwsl_err("%s: key bits %d less than minimum %d\n",
|
||||
__func__, RSA_size(rsapub) * 8, rsa_min_bits);
|
||||
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
#if defined(LWS_HAVE_RSA_SET0_KEY)
|
||||
/* we don't need d... but the api wants to write it */
|
||||
RSA_get0_key(rsapub,
|
||||
(const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_N],
|
||||
(const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_E],
|
||||
(const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_D]);
|
||||
#else
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = rsapub->e;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = rsapub->n;
|
||||
mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = NULL;
|
||||
#endif
|
||||
count = LWS_GENCRYPTO_RSA_KEYEL_D;
|
||||
n = LWS_GENCRYPTO_RSA_KEYEL_E;
|
||||
break;
|
||||
default:
|
||||
lwsl_err("%s: unknown NID\n", __func__);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
for (; n < count; n++) {
|
||||
if (!mpi[n])
|
||||
continue;
|
||||
jwk->e[n].len = BN_num_bytes(mpi[n]);
|
||||
jwk->e[n].buf = lws_malloc(jwk->e[n].len, "certkeyimp");
|
||||
if (!jwk->e[n].buf) {
|
||||
if (id == NID_X9_62_id_ecPublicKey) {
|
||||
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
|
||||
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
|
||||
}
|
||||
goto bail2;
|
||||
}
|
||||
BN_bn2bin(mpi[n], jwk->e[n].buf);
|
||||
}
|
||||
|
||||
if (id == NID_X9_62_id_ecPublicKey) {
|
||||
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
|
||||
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail2:
|
||||
if (id == NID_X9_62_id_ecPublicKey)
|
||||
EC_KEY_free(ecpub);
|
||||
else
|
||||
RSA_free(rsapub);
|
||||
|
||||
bail1:
|
||||
EVP_PKEY_free(pkey);
|
||||
bail:
|
||||
/* jwk destroy will clean any partial state */
|
||||
if (ret)
|
||||
lws_jwk_destroy(jwk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_x509_jwk_privkey_pem_pp_cb(char *buf, int size, int rwflag, void *u)
|
||||
{
|
||||
const char *pp = (const char *)u;
|
||||
int n = strlen(pp);
|
||||
|
||||
if (n > size - 1)
|
||||
return -1;
|
||||
|
||||
memcpy(buf, pp, n + 1);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len,
|
||||
const char *passphrase)
|
||||
{
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIGNUM *mpi, *dummy[4];
|
||||
EVP_PKEY *pkey = NULL;
|
||||
const BIGNUM *cmpi;
|
||||
int n, m, ret = -1;
|
||||
EC_KEY *ecpriv;
|
||||
RSA *rsapriv;
|
||||
|
||||
BIO_write(bio, pem, len);
|
||||
PEM_read_bio_PrivateKey(bio, &pkey, lws_x509_jwk_privkey_pem_pp_cb,
|
||||
(void *)passphrase);
|
||||
BIO_free(bio);
|
||||
lws_explicit_bzero((void *)pem, len);
|
||||
if (!pkey) {
|
||||
lwsl_err("%s: unable to parse PEM privkey\n", __func__);
|
||||
lws_tls_err_describe();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* confirm the key type matches the existing jwk situation */
|
||||
|
||||
switch (jwk->kty) {
|
||||
case LWS_GENCRYPTO_KTY_EC:
|
||||
if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
|
||||
lwsl_err("%s: jwk is EC but privkey isn't\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
ecpriv = EVP_PKEY_get1_EC_KEY(pkey);
|
||||
if (!ecpriv) {
|
||||
lwsl_notice("%s: missing EC key\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
cmpi = EC_KEY_get0_private_key(ecpriv);
|
||||
|
||||
/* quick size check first */
|
||||
|
||||
n = BN_num_bytes(cmpi);
|
||||
if (jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != (uint32_t)n) {
|
||||
lwsl_err("%s: jwk key size doesn't match\n", __func__);
|
||||
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
/* TODO.. check public curve / group + point */
|
||||
|
||||
jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len = n;
|
||||
jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf = lws_malloc(n, "ec");
|
||||
if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf)
|
||||
goto bail1;
|
||||
|
||||
m = BN_bn2binpad(cmpi, jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf,
|
||||
jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len);
|
||||
if (m != BN_num_bytes(cmpi))
|
||||
goto bail1;
|
||||
|
||||
break;
|
||||
|
||||
case LWS_GENCRYPTO_KTY_RSA:
|
||||
if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA) {
|
||||
lwsl_err("%s: RSA jwk, non-RSA privkey\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
rsapriv = EVP_PKEY_get1_RSA(pkey);
|
||||
if (!rsapriv) {
|
||||
lwsl_notice("%s: missing RSA key\n", __func__);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
|
||||
#if defined(LWS_HAVE_RSA_SET0_KEY)
|
||||
RSA_get0_key(rsapriv, (const BIGNUM **)&dummy[0], /* n */
|
||||
(const BIGNUM **)&dummy[1], /* e */
|
||||
(const BIGNUM **)&mpi); /* d */
|
||||
#else
|
||||
dummy[0] = rsapriv->n;
|
||||
dummy[1] = rsapriv->e;
|
||||
mpi = rsapriv->d;
|
||||
#endif
|
||||
|
||||
/* quick size check first */
|
||||
|
||||
n = BN_num_bytes(mpi);
|
||||
if (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len != (uint32_t)n) {
|
||||
lwsl_err("%s: jwk key size doesn't match\n", __func__);
|
||||
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
/* then check that n & e match what we got from the cert */
|
||||
|
||||
dummy[2] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf,
|
||||
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len,
|
||||
NULL);
|
||||
dummy[3] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf,
|
||||
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len,
|
||||
NULL);
|
||||
|
||||
m = BN_cmp(dummy[2], dummy[0]) | BN_cmp(dummy[3], dummy[1]);
|
||||
BN_clear_free(dummy[2]);
|
||||
BN_clear_free(dummy[3]);
|
||||
if (m) {
|
||||
lwsl_err("%s: privkey doesn't match jwk pubkey\n",
|
||||
__func__);
|
||||
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
/* accept d from the PEM privkey into the JWK */
|
||||
|
||||
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].len = n;
|
||||
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf = lws_malloc(n, "privjk");
|
||||
if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf)
|
||||
goto bail;
|
||||
|
||||
BN_bn2bin(mpi, jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf);
|
||||
break;
|
||||
default:
|
||||
lwsl_err("%s: JWK has unknown kty %d\n", __func__, jwk->kty);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail1:
|
||||
if (jwk->kty == LWS_GENCRYPTO_KTY_EC)
|
||||
EC_KEY_free(ecpriv);
|
||||
else
|
||||
RSA_free(rsapriv);
|
||||
|
||||
bail:
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
lws_x509_destroy(struct lws_x509_cert **x509)
|
||||
{
|
||||
if (!*x509)
|
||||
return;
|
||||
|
||||
if ((*x509)->cert) {
|
||||
X509_free((*x509)->cert);
|
||||
(*x509)->cert = NULL;
|
||||
}
|
||||
|
||||
lws_free_set_NULL(*x509);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -167,6 +167,11 @@ struct lws_lws_tls {
|
|||
unsigned int redirect_to_https:1;
|
||||
};
|
||||
|
||||
struct lws_ec_valid_curves {
|
||||
int id;
|
||||
const char *jwa_name; /* list terminates with NULL jwa_name */
|
||||
};
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_context_init_alpn(struct lws_vhost *vhost);
|
||||
LWS_EXTERN enum lws_tls_extant
|
||||
|
@ -312,4 +317,8 @@ lws_genec_destroy_elements(struct lws_gencrypto_keyelem *el);
|
|||
int
|
||||
lws_gencrypto_mbedtls_rngf(void *context, unsigned char *buf, size_t len);
|
||||
|
||||
int
|
||||
lws_genec_confirm_curve_allowed_by_tls_id(const char *allowed, int id,
|
||||
struct lws_jwk *jwk);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,4 +2,6 @@
|
|||
---|---
|
||||
minimal-crypto-jwe|Examples for lws RFC7516 JWE apis
|
||||
minimal-crypto-jwk|Examples for lws RFC7517 JWK apis
|
||||
minimal-crypto-jws|Examples for lws RFC7515 JWS apis
|
||||
minimal-crypto-x509|Examples for lws X.509 apis
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ Commandline option|Meaning
|
|||
-e "<cek cipher alg> <payload enc alg>"|Encrypt (default is decrypt), eg, -e "RSA1_5 A128CBC-HS256". For decrypt, the cipher information comes from the input JWE.
|
||||
-k <jwk file>|JWK file to encrypt or decrypt with
|
||||
-c|Format the JWE as a linebroken C string
|
||||
-f|Output flattened representation (instead of compact by default)
|
||||
|
||||
```
|
||||
$ echo -n "plaintext0123456" | ./lws-crypto-jwe -k key-rsa-4096.private -e "RSA1_5 A128CBC-HS256"
|
||||
|
|
|
@ -76,11 +76,14 @@ format_c(const char *key)
|
|||
}
|
||||
}
|
||||
|
||||
#define MAX_SIZE (4 * 1024 * 1024)
|
||||
char temp[MAX_SIZE], compact[MAX_SIZE];
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n, enc = 0, result = 0,
|
||||
logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
char temp[32768], compact[32768], *in;
|
||||
char *in;
|
||||
struct lws_context_creation_info info;
|
||||
int temp_len = sizeof(temp);
|
||||
struct lws_context *context;
|
||||
|
@ -132,7 +135,7 @@ int main(int argc, const char **argv)
|
|||
if (lws_jws_alloc_element(&jwe.jws.map, LJWS_JOSE,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len, strlen(p) +
|
||||
strlen(sp + 1) + 16, 0)) {
|
||||
strlen(sp + 1) + 32, 0)) {
|
||||
lwsl_err("%s: temp space too small\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
@ -189,19 +192,23 @@ int main(int argc, const char **argv)
|
|||
|
||||
/* perform the encryption of the CEK and the plaintext */
|
||||
|
||||
n = lws_jwe_encrypt(&jwe,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
n = lws_jwe_encrypt(&jwe, lws_concat_temp(temp, temp_len),
|
||||
&temp_len);
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
|
||||
goto bail1;
|
||||
}
|
||||
if (lws_cmdline_option(argc, argv, "-f"))
|
||||
/* output the JWE in flattened form */
|
||||
n = lws_jwe_render_flattened(&jwe, compact,
|
||||
sizeof(compact));
|
||||
else
|
||||
/* output the JWE in compact form */
|
||||
n = lws_jwe_render_compact(&jwe, compact,
|
||||
sizeof(compact));
|
||||
|
||||
/* output the JWE in compact form */
|
||||
|
||||
n = lws_jwe_render_compact(&jwe, compact, sizeof(compact));
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: lws_jwe_render_compact failed: %d\n",
|
||||
lwsl_err("%s: lws_jwe_render failed: %d\n",
|
||||
__func__, n);
|
||||
goto bail1;
|
||||
}
|
||||
|
@ -214,18 +221,27 @@ int main(int argc, const char **argv)
|
|||
goto bail1;
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* converts a compact serialization to b64 + decoded maps
|
||||
* held in jws
|
||||
*/
|
||||
if (lws_jws_compact_decode(in, n, &jwe.jws.map, &jwe.jws.map_b64,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len) != 5) {
|
||||
lwsl_err("%s: lws_jws_compact_decode failed\n",
|
||||
__func__);
|
||||
goto bail1;
|
||||
}
|
||||
if (lws_cmdline_option(argc, argv, "-f")) {
|
||||
if (lws_jwe_json_parse(&jwe, (uint8_t *)in, n,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len)) {
|
||||
lwsl_err("%s: lws_jws_compact_decode failed\n",
|
||||
__func__);
|
||||
goto bail1;
|
||||
}
|
||||
} else
|
||||
/*
|
||||
* converts a compact serialization to b64 + decoded maps
|
||||
* held in jws
|
||||
*/
|
||||
if (lws_jws_compact_decode(in, n, &jwe.jws.map,
|
||||
&jwe.jws.map_b64,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len) != 5) {
|
||||
lwsl_err("%s: lws_jws_compact_decode failed\n",
|
||||
__func__);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the crypto according to what we parsed into the jose
|
||||
|
|
|
@ -41,6 +41,7 @@ Commandline option|Meaning
|
|||
-s "<signature alg>"|Sign (default is verify), eg, -e "ES256". For verify, the cipher information comes from the input JWS.
|
||||
-k <jwk file>|JWK file to sign or verify with... sign requires the key has its private part
|
||||
-c|Format the JWE as a linebroken C string
|
||||
-f|Output flattened representation (instead of compact by default)
|
||||
|
||||
```
|
||||
$ echo -n "plaintext0123456" | ./lws-crypto-jws -s "ES256" -k ec-p256.private
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define MAX_SIZE (4 * 1024 * 1024)
|
||||
char temp[MAX_SIZE], compact[MAX_SIZE];
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n, sign = 0, result = 0,
|
||||
logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
char temp[32768], compact[32768], *in;
|
||||
char *in;
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_jws_map map;
|
||||
int temp_len = sizeof(temp);
|
||||
|
@ -92,7 +94,6 @@ int main(int argc, const char **argv)
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sign) {
|
||||
|
||||
/* add the plaintext from stdin to the map and a b64 version */
|
||||
|
@ -116,7 +117,7 @@ int main(int argc, const char **argv)
|
|||
|
||||
/* prepare the space for the b64 signature in the map */
|
||||
|
||||
if (lws_jws_alloc_element(&jws.map, LJWS_SIG,
|
||||
if (lws_jws_alloc_element(&jws.map_b64, LJWS_SIG,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len, lws_base64_size(
|
||||
LWS_JWE_LIMIT_KEY_ELEMENT_BYTES), 0)) {
|
||||
|
@ -124,6 +125,8 @@ int main(int argc, const char **argv)
|
|||
goto bail1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* sign the plaintext */
|
||||
|
||||
n = lws_jws_sign_from_b64(&jose, &jws,
|
||||
|
@ -136,9 +139,12 @@ int main(int argc, const char **argv)
|
|||
/* set the actual b64 signature size */
|
||||
jws.map_b64.len[LJWS_SIG] = n;
|
||||
|
||||
/* create the compact JWS representation */
|
||||
|
||||
n = lws_jws_write_compact(&jws, compact, sizeof(compact));
|
||||
if (lws_cmdline_option(argc, argv, "-f"))
|
||||
/* create the flattened representation */
|
||||
n = lws_jws_write_flattened_json(&jws, compact, sizeof(compact));
|
||||
else
|
||||
/* create the compact JWS representation */
|
||||
n = lws_jws_write_compact(&jws, compact, sizeof(compact));
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: write_compact failed\n", __func__);
|
||||
goto bail1;
|
||||
|
@ -154,13 +160,31 @@ int main(int argc, const char **argv)
|
|||
} else {
|
||||
/* perform the verify directly on the compact representation */
|
||||
|
||||
if (lws_jws_sig_confirm_compact_b64(in,
|
||||
lws_concat_used(temp, temp_len),
|
||||
&map, &jwk, context,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len) < 0) {
|
||||
lwsl_notice("%s: confirm rsa sig failed\n", __func__);
|
||||
goto bail1;
|
||||
if (lws_cmdline_option(argc, argv, "-f")) {
|
||||
if (lws_jws_sig_confirm_json(in, n, &jws, &jwk, context,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len) < 0) {
|
||||
lwsl_notice("%s: confirm rsa sig failed\n",
|
||||
__func__);
|
||||
lwsl_hexdump_notice(jws.map.buf[LJWS_JOSE], jws.map.len[LJWS_JOSE]);
|
||||
lwsl_hexdump_notice(jws.map.buf[LJWS_PYLD], jws.map.len[LJWS_PYLD]);
|
||||
lwsl_hexdump_notice(jws.map.buf[LJWS_SIG], jws.map.len[LJWS_SIG]);
|
||||
|
||||
lwsl_hexdump_notice(jws.map_b64.buf[LJWS_JOSE], jws.map_b64.len[LJWS_JOSE]);
|
||||
lwsl_hexdump_notice(jws.map_b64.buf[LJWS_PYLD], jws.map_b64.len[LJWS_PYLD]);
|
||||
lwsl_hexdump_notice(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]);
|
||||
goto bail1;
|
||||
}
|
||||
} else {
|
||||
if (lws_jws_sig_confirm_compact_b64(in,
|
||||
lws_concat_used(temp, temp_len),
|
||||
&map, &jwk, context,
|
||||
lws_concat_temp(temp, temp_len),
|
||||
&temp_len) < 0) {
|
||||
lwsl_notice("%s: confirm rsa sig failed\n",
|
||||
__func__);
|
||||
goto bail1;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_notice("VALID\n");
|
||||
|
|
77
minimal-examples/crypto/minimal-crypto-x509/CMakeLists.txt
Normal file
77
minimal-examples/crypto/minimal-crypto-x509/CMakeLists.txt
Normal file
|
@ -0,0 +1,77 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-crypto-x509)
|
||||
set(SRCS main.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_WITH_JOSE 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
59
minimal-examples/crypto/minimal-crypto-x509/README.md
Normal file
59
minimal-examples/crypto/minimal-crypto-x509/README.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
# lws minimal example for X509
|
||||
|
||||
The example shows how to:
|
||||
|
||||
- confirm one PEM cert or chain (-c) was signed by a trusted PEM cert (-t)
|
||||
- convert a certificate public key to JWK
|
||||
- convert a certificate public key and its private key PEM to a private JWK
|
||||
|
||||
The examples work for EC and RSA certs and on mbedtls and OpenSSL the same.
|
||||
|
||||
Notice the logging is on stderr, and only the JWK is output on stdout.
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-c <PEM certificate path>|Required PEM Certificate(s) to operate on... may be multiple concatednated PEM
|
||||
-t <PEM certificate path>|Single PEM trusted certificate
|
||||
-p <PEM private key path>|Optional private key matching certificate given in -c. If given, only the private JWK is printed to stdout
|
||||
|
||||
Example for confirming trust relationship. Notice the PEM in -c must contain not only
|
||||
the final certificate but also the certificates for any intermediate CAs.
|
||||
|
||||
```
|
||||
$ ./lws-crypto-x509 -c ec-cert.pem -t ca-cert.pem
|
||||
[2019/01/02 20:31:13:2031] USER: LWS X509 api example
|
||||
[2019/01/02 20:31:13:2032] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
|
||||
[2019/01/02 20:31:13:2043] NOTICE: main: certs loaded OK
|
||||
[2019/01/02 20:31:13:2043] NOTICE: main: verified OK <<<<======
|
||||
[2019/01/02 20:31:13:2045] NOTICE: Cert Public JWK
|
||||
{"crv":"P-521","kty":"EC","x":"_uRNBbIbm0zhk8v6ujvQX9924264ZkqJhit0qamAoCegzuJbLf434kN7_aFEt6u-QWUu6-N1R8t6OlvrLo2jrNY","y":"AU-29XpNyB7e5e3s5t0ylzGEnF601A8A7Tx8m8xxngARZX_bn22itGJ3Y57BTcclPMoG80KjWAMnRVtrKqrD_aGD"}
|
||||
|
||||
[2019/01/02 20:31:13:2045] NOTICE: main: OK
|
||||
```
|
||||
|
||||
Example creating JWKs for public and public + private cert + PEM keys:
|
||||
|
||||
```
|
||||
$ ./lws-crypto-x509 -c ec-cert.pem -p ec-key.pem
|
||||
[2019/01/02 20:14:43:4966] USER: LWS X509 api example
|
||||
[2019/01/02 20:14:43:5225] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
|
||||
[2019/01/02 20:14:43:5707] NOTICE: lws_x509_public_to_jwk: EC key
|
||||
[2019/01/02 20:24:59:9514] USER: LWS X509 api example
|
||||
[2019/01/02 20:24:59:9741] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
|
||||
[2019/01/02 20:25:00:1261] NOTICE: lws_x509_public_to_jwk: key type 408 "id-ecPublicKey"
|
||||
[2019/01/02 20:25:00:1269] NOTICE: lws_x509_public_to_jwk: EC key
|
||||
[2019/01/02 20:25:00:2097] NOTICE: Cert + Key Private JWK
|
||||
{"crv":"P-521","d":"AU3iQSKfPskMTW4ZncrYLhipUYzLYty2XhemTQ_nSuUB1vB76jHmOYUTRXFBLkVCW8cQYyMa5dMa3Bvv-cdvH0IB","kty":"EC","x":"_uRNBbIbm0zhk8v6ujvQX9924264ZkqJhit0qamAoCegzuJbLf434kN7_aFEt6u-QWUu6-N1R8t6OlvrLo2jrNY","y":"AU-29XpNyB7e5e3s5t0ylzGEnF601A8A7Tx8m8xxngARZX_bn22itGJ3Y57BTcclPMoG80KjWAMnRVtrKqrD_aGD"}
|
||||
|
||||
[2019/01/02 20:25:00:2207] NOTICE: main: OK
|
||||
```
|
||||
|
191
minimal-examples/crypto/minimal-crypto-x509/main.c
Normal file
191
minimal-examples/crypto/minimal-crypto-x509/main.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* lws-crypto-x509
|
||||
*
|
||||
* Copyright (C) 2018 - 2019 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>
|
||||
#include <errno.h>
|
||||
|
||||
static int
|
||||
read_pem(const char *filename, char *pembuf, int pembuf_len)
|
||||
{
|
||||
int n, fd = open(filename, LWS_O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
n = read(fd, pembuf, pembuf_len - 1);
|
||||
close(fd);
|
||||
|
||||
pembuf[n++] = '\0';
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
read_pem_c509_cert(struct lws_x509_cert **x509, const char *filename,
|
||||
char *pembuf, int pembuf_len)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = read_pem(filename, pembuf, pembuf_len);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
if (lws_x509_create(x509)) {
|
||||
lwsl_err("%s: failed to create x509\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lws_x509_parse_from_pem(*x509, pembuf, n) < 0) {
|
||||
lwsl_err("%s: unable to parse PEM %s\n", __func__, filename);
|
||||
lws_x509_destroy(x509);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n, result = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_x509_cert *x509 = NULL, *x509_trusted = NULL;
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
struct lws_jwk jwk;
|
||||
char pembuf[6144];
|
||||
const char *p;
|
||||
|
||||
memset(&jwk, 0, sizeof(jwk));
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS X509 api example\n");
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.options = 0;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
p = lws_cmdline_option(argc, argv, "-c");
|
||||
if (!p) {
|
||||
lwsl_err("%s: missing -c <cert pem file>\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
if (read_pem_c509_cert(&x509, p, pembuf, sizeof(pembuf))) {
|
||||
lwsl_err("%s: unable to read \"%s\": errno %d\n",
|
||||
__func__, p, errno);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
p = lws_cmdline_option(argc, argv, "-t");
|
||||
if (p) {
|
||||
|
||||
if (read_pem_c509_cert(&x509_trusted, p, pembuf,
|
||||
sizeof(pembuf))) {
|
||||
lwsl_err("%s: unable to read \"%s\": errno %d\n",
|
||||
__func__, p, errno);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lwsl_notice("%s: certs loaded OK\n", __func__);
|
||||
|
||||
if (lws_x509_verify(x509, x509_trusted, NULL)) {
|
||||
lwsl_err("%s: verify failed\n", __func__);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
lwsl_notice("%s: verified OK\n", __func__);
|
||||
}
|
||||
|
||||
if (x509_trusted) {
|
||||
|
||||
/* show the trusted cert public key as a JWK */
|
||||
|
||||
if (lws_x509_public_to_jwk(&jwk, x509_trusted,
|
||||
"P-256,P-384,P-521", 4096)) {
|
||||
lwsl_err("%s: unable to get trusted cert pubkey as JWK\n",
|
||||
__func__);
|
||||
|
||||
goto bail2;
|
||||
}
|
||||
lwsl_info("JWK version of trusted cert:\n");
|
||||
lws_jwk_dump(&jwk);
|
||||
lws_jwk_destroy(&jwk);
|
||||
}
|
||||
|
||||
/* get the cert public key as a JWK */
|
||||
|
||||
if (lws_x509_public_to_jwk(&jwk, x509, "P-256,P-384,P-521", 4096)) {
|
||||
lwsl_err("%s: unable to get cert pubkey as JWK\n", __func__);
|
||||
|
||||
goto bail3;
|
||||
}
|
||||
lwsl_info("JWK version of cert:\n");
|
||||
lws_jwk_dump(&jwk);
|
||||
/* only print public if he doesn't provide private */
|
||||
if (!lws_cmdline_option(argc, argv, "-p")) {
|
||||
lwsl_notice("Issuing Cert Public JWK on stdout\n");
|
||||
n = sizeof(pembuf);
|
||||
if (lws_jwk_export(&jwk, 0, pembuf, &n))
|
||||
puts(pembuf);
|
||||
}
|
||||
|
||||
/* if we know where the cert private key is, add that to the cert JWK */
|
||||
|
||||
p = lws_cmdline_option(argc, argv, "-p");
|
||||
if (p) {
|
||||
n = read_pem(p, pembuf, sizeof(pembuf));
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: unable read privkey %s\n", __func__, p);
|
||||
|
||||
goto bail3;
|
||||
}
|
||||
if (lws_x509_jwk_privkey_pem(&jwk, pembuf, n, NULL)) {
|
||||
lwsl_err("%s: unable to parse privkey %s\n",
|
||||
__func__, p);
|
||||
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
lwsl_info("JWK version of cert + privkey:\n");
|
||||
lws_jwk_dump(&jwk);
|
||||
lwsl_notice("Issuing Cert + Private JWK on stdout\n");
|
||||
n = sizeof(pembuf);
|
||||
if (lws_jwk_export(&jwk, 1, pembuf, &n))
|
||||
puts(pembuf);
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
bail3:
|
||||
lws_jwk_destroy(&jwk);
|
||||
bail2:
|
||||
lws_x509_destroy(&x509_trusted);
|
||||
bail1:
|
||||
lws_x509_destroy(&x509);
|
||||
bail:
|
||||
lws_context_destroy(context);
|
||||
|
||||
if (result)
|
||||
lwsl_err("%s: failed\n", __func__);
|
||||
else
|
||||
lwsl_notice("%s: OK\n", __func__);
|
||||
|
||||
return result;
|
||||
}
|
Loading…
Add table
Reference in a new issue