1
0
Fork 0
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:
Andy Green 2018-12-31 20:35:54 +08:00
parent 21889b53f7
commit 0adc845507
35 changed files with 2025 additions and 615 deletions

View file

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

View file

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

View file

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

View file

@ -90,7 +90,7 @@ enum lws_gencrypto_aes_tok {
struct lws_gencrypto_keyelem {
uint8_t *buf;
uint16_t len;
uint32_t len;
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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++ = '\"';

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View 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
```

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