1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00
This commit is contained in:
Andy Green 2018-12-13 20:05:12 +08:00
parent a3dcc95471
commit eda102e397
81 changed files with 8280 additions and 1631 deletions

1
.gitignore vendored
View file

@ -51,3 +51,4 @@ doc
/build3/
/cov-int/
/.vs/
/build-mtls/

View file

@ -4,10 +4,10 @@ env:
global:
- secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
matrix:
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_JWS=1 -DLWS_WITH_GENRSA=1 -DLWS_WITH_GENHASH=1 -DLWS_WITH_GENAES=1 -DLWS_WITH_GENEC=1"
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/ -DLWS_WITH_GENCRYPTO=1 -DLWS_WITH_JOSE=1"
- LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_ROLE_DBUS=1 -DLWS_DBUS_INCLUDE2=/usr/lib/x86_64-linux-gnu/dbus-1.0/include/"
- LWS_METHOD=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
- LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_JOSE=1 -DCMAKE_BUILD_TYPE=DEBUG"
- LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"

View file

@ -105,12 +105,8 @@ option(LWS_WITH_SMTP "Provide SMTP support" OFF)
option(LWS_WITH_NO_LOGS "Disable all logging from being compiled in" OFF)
option(LWS_AVOID_SIGPIPE_IGN "Android 7+ reportedly needs this" OFF)
option(LWS_WITH_STATS "Keep statistics of lws internal operations" OFF)
option(LWS_WITH_JWS "JSON Web Signature (RFC7515) API" OFF)
option(LWS_WITH_JWE "JSON Web Encryption (RFC7516) API" OFF)
option(LWS_WITH_GENHASH "Enable support for Generic Hash (SHA1 + SHA2 with api independent of TLS backend)" OFF)
option(LWS_WITH_GENRSA "Enable support for Generic RSA (RSA with api independent of TLS backend)" OFF)
option(LWS_WITH_GENAES "Enable support for Generic AES (AES with api independent of TLS backend)" OFF)
option(LWS_WITH_GENEC "Enable support for Generic EC (EC with api independent of TLS backend)" OFF)
option(LWS_WITH_JOSE "JSON Web Signature / Encryption / Keys (RFC7515/6/) API" OFF)
option(LWS_WITH_GENCRYPTO "Enable support for Generic Crypto apis independent of TLS backend" OFF)
option(LWS_WITH_SELFTESTS "Selftests run at context creation" OFF)
option(LWS_WITH_GCOV "Build with gcc gcov coverage instrumentation" OFF)
option(LWS_WITH_EXPORT_LWSTARGETS "Export libwebsockets CMake targets. Disable if they conflict with an outer cmake project." ON)
@ -159,11 +155,8 @@ if(LWS_WITH_DISTRO_RECOMMENDED)
set(LWS_WITH_LEJP_CONF 1)
set(LWS_WITH_PLUGINS 1)
set(LWS_ROLE_RAW_PROXY 1)
set(LWS_WITH_GENHASH 1)
set(LWS_WITH_GENRSA 1)
set(LWS_WITH_GENEC 1)
set(LWS_WITH_JWS 1)
set(LWS_WITH_JWE 1)
set(LWS_WITH_GENCRYPTO 1)
set(LWS_WITH_JOSE 1)
endif()
# do you care about this? Then send me a patch where it disables it on travis
@ -292,6 +285,11 @@ if (LWS_WITH_LWSWS)
set(LWS_ROLE_RAW_PROXY 1)
endif()
# sshd plugin
if (LWS_WITH_PLUGINS)
set(LWS_WITH_GENCRYPTO 1)
endif()
if (LWS_ROLE_RAW_PROXY)
set (LWS_WITHOUT_CLIENT 0)
set (LWS_WITHOUT_SERVER 0)
@ -300,19 +298,12 @@ endif()
if (LWS_WITH_ACME)
set (LWS_WITHOUT_CLIENT 0)
set (LWS_WITHOUT_SERVER 0)
set (LWS_WITH_JWS 1)
set (LWS_WITH_JWE 1)
set (LWS_WITH_JOSE 1)
endif()
if (LWS_WITH_JWE)
set(LWS_WITH_JWS 1)
endif()
if (LWS_WITH_JWS)
if (LWS_WITH_JOSE)
set(LWS_WITH_LEJP 1)
set(LWS_WITH_GENHASH 1)
set(LWS_WITH_GENRSA 1)
set(LWS_WITH_GENEC 1)
set(LWS_WITH_GENCRYPTO 1)
endif()
if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV)
@ -327,8 +318,7 @@ endif()
if (LWS_WITH_PLUGINS OR LWS_WITH_CGI)
# sshd plugin
set(LWS_WITH_GENHASH 1)
set(LWS_WITH_GENRSA 1)
set(LWS_WITH_GENCRYPTO 1)
endif()
if (LWS_WITH_GENERIC_SESSIONS)
@ -1025,29 +1015,13 @@ if (LWS_WITH_SSL)
list(APPEND SOURCES
lib/tls/mbedtls/ssl.c
)
if (LWS_WITH_GENHASH)
if (LWS_WITH_GENCRYPTO)
list(APPEND SOURCES
lib/tls/mbedtls/lws-genhash.c
)
endif()
if (LWS_WITH_GENRSA)
list(APPEND SOURCES
lib/tls/mbedtls/lws-genrsa.c
)
endif()
if (LWS_WITH_GENAES)
list(APPEND SOURCES
lib/tls/mbedtls/lws-genaes.c
)
endif()
if (LWS_WITH_GENEC)
list(APPEND SOURCES
lib/tls/lws-genec-common.c
lib/tls/mbedtls/lws-genec.c
)
endif()
if (LWS_WITH_GENEC OR LWS_WITH_GENRSA)
list(APPEND SOURCES
lib/tls/mbedtls/lws-gencrypto.c
)
endif()
@ -1055,29 +1029,13 @@ if (LWS_WITH_SSL)
list(APPEND SOURCES
lib/tls/openssl/ssl.c
)
if (LWS_WITH_GENHASH)
if (LWS_WITH_GENCRYPTO)
list(APPEND SOURCES
lib/tls/openssl/lws-genhash.c
)
endif()
if (LWS_WITH_GENRSA)
list(APPEND SOURCES
lib/tls/openssl/lws-genrsa.c
)
endif()
if (LWS_WITH_GENAES)
list(APPEND SOURCES
lib/tls/openssl/lws-genaes.c
)
endif()
if (LWS_WITH_GENEC)
list(APPEND SOURCES
lib/tls/lws-genec-common.c
lib/tls/openssl/lws-genec.c
)
endif()
if (LWS_WITH_GENEC OR LWS_WITH_GENRSA)
list(APPEND SOURCES
lib/tls/openssl/lws-gencrypto.c
)
endif()
@ -1235,19 +1193,19 @@ if (LWS_WITH_ZIP_FOPS)
endif()
endif()
if (LWS_WITH_JWS)
if (LWS_WITH_JOSE)
list(APPEND SOURCES
lib/jose/jwk/jwk.c
lib/jose/jws/jose.c
lib/jose/jws/jws.c)
lib/jose/jws/jws.c
lib/jose/jwe/jwe.c
lib/jose/jwe/jwe-rsa-aescbc.c
lib/jose/jwe/jwe-aeskw.c
lib/jose/jwe/jwe-rsa-aesgcm.c
)
endif()
if (LWS_WITH_JWE)
list(APPEND SOURCES
lib/jose/jwe/jwe.c)
endif()
if (LWS_WITH_JWE OR LWS_WITH_JWS OR LWS_WITH_GENHASH)
if (LWS_WITH_JOSE OR LWS_WITH_GENCRYPTO)
list(APPEND SOURCES
lib/tls/lws-gencrypto-common.c)
endif()
@ -1685,6 +1643,9 @@ CHECK_FUNCTION_EXISTS(SSL_CTX_set_ciphersuites LWS_HAVE_SSL_CTX_set_ciphersuites
if (LWS_WITH_SSL AND NOT LWS_WITH_MBEDTLS)
CHECK_SYMBOL_EXISTS(SSL_CTX_get_extra_chain_certs_only openssl/ssl.h LWS_HAVE_SSL_EXTRA_CHAIN_CERTS)
CHECK_FUNCTION_EXISTS(EVP_MD_CTX_free LWS_HAVE_EVP_MD_CTX_free)
CHECK_FUNCTION_EXISTS(ECDSA_SIG_set0 LWS_HAVE_ECDSA_SIG_set0)
CHECK_FUNCTION_EXISTS(BN_bn2binpad LWS_HAVE_BN_bn2binpad)
CHECK_FUNCTION_EXISTS(EVP_aes_128_wrap LWS_HAVE_EVP_aes_128_wrap)
endif()
if (LWS_WITH_MBEDTLS)
set(LWS_HAVE_TLS_CLIENT_METHOD 1)

View file

@ -11,14 +11,56 @@ cloud serving.
[50 minimal examples](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples) for
various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to get started quickly.
![overview](./doc-assets/lws-overview.svg)
![overview](./doc-assets/lws-overview.png)
News
----
## New features on master
- **`lws-genec` ECDH + ECDSA** - Work in progress
- 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).
- **`JWE`** - JWE (RFC7516) Work in progress: Working CI tests
|Key Encryption|Payload authentication + crypt|Enc + Dec Support|
|---|---|---|
|`RSAES-PKCS1-v1.5` 2048b & 4096b|`AES_128_CBC_HMAC_SHA_256`|Enc + Dec|
|`RSAES-PKCS1-v1.5` 2048b|`AES_192_CBC_HMAC_SHA_384`|Enc + Dec|
|`RSAES-PKCS1-v1.5` 2048b|`AES_256_CBC_HMAC_SHA_512`|Enc + Dec|
|`RSAES-OAEP`|`AES_256_GCM`|Enc + Dec|
|`AES128KW`, `AES192KW`, `AES256KW`|`AES_128_CBC_HMAC_SHA_256`|Enc + Dec|
|`AES128KW`, `AES192KW`, `AES256KW`|`AES_192_CBC_HMAC_SHA_384`|Enc + Dec|
|`AES128KW`, `AES192KW`, `AES256KW`|`AES_256_CBC_HMAC_SHA_512`|Enc + Dec|
|`ECDH-ES` P-256|`AES_128_GCM`|Dec|
All tests pass on both OpenSSL and mbedTLS backends, using keys generated on
both OpenSSL and mbedTLS in the tests.
A minimal example tool shows how to encrypt and decrypt compact JWE objects
from the commandline for all supported algorithms.
[jwe api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jwe.h),
[jwe unit tests](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-jose/jwe.c),
[jwe minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
- **`lws-genec` ECDSA** - JWS-compatible ECDSA is supported on both OpenSSL and mbedtls... Work in progress: ECDH-ES
- **`JWS`** - JWS (RFC7515) is now supported for none, HS256/384/512, RS256/384/512, and ES256/384/512, on both OpenSSL and mbedtls. There's a minimal example tool that signs and verifies compact
representation JWS from stdin.
[jws api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jws.h),
[jws unit tests](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-jose/jws.c),
[jws minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
- **`JWK`** - JWK (RFC7517) now supports oct, RSA and EC keys including JSON key
arrays on both OpenSSL and mbedtls. A minimal example tool shows how to create
new JSON JWK keys to specified parameters from the commandline for all supported
ciphers.
[jwk minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwk)
- **`lws-genrsa` OAEP + PSS support** - in addition to PKCS#1 1.5 padding, OAEP and PSS are
now supported on both mbedtls and openssl backends.
@ -27,7 +69,7 @@ News
backends. Supports CBC, CFB128, CFB8, CTR, ECB, OFB, XTS and GCM variants. Unit tests in CI.
[genaes api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genaes.h),
[api test](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-gencrypto),
CMake config: `-DLWS_WITH_GENAES=1`
CMake config: `-DLWS_WITH_GENCRYPTO=1`
- **http fallback support** - you can specify a role and protocol to apply if non-http or non-tls
packets arrive at an http(s) listen port. For example, you can specify that the new `raw proxy`

View file

@ -15,6 +15,19 @@ or via another layer on top, which processes JOSE JSON objects using
JWS (JSON Web Signatures), JWK (JSON Web Keys), and JWE (JSON Web
Encryption).
The `JW` apis use the generic apis (`lws_genrsa_`, etc) to get the crypto tasks
done, so anything they can do you can also get done using the generic apis.
The main difference is that with the generic apis, you must instantiate the
correct types and use type-specfic apis. With the `JW` apis, there is only
one interface for all operations, with the details hidden in the api and
controlled by the JSON objects.
Because of this, the `JW` apis are often preferred because they give you
"crypto agility" cheaply... to change your crypto to another supported algorithm
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).
## Using the generic layer
All the necessary includes are part of `libwebsockets.h`.
@ -63,7 +76,7 @@ Unit tests for these apis, which serve as usage examples, can be found in [./min
Keys in the JOSE layer use a `struct lws_jwk`, this contains two arrays of
`struct lws_jwk_elements` sized for the worst case (currently RSA). One
array contains the key elements as described for the generic case, and the
other contains various key metadata taken from JWK JSON.
other contains various nonencrypted key metadata taken from JWK JSON.
|metadata index|function|
|---|---|
@ -74,3 +87,6 @@ other contains various key metadata taken from JWK JSON.
|`JWK_META_X5C`|Optional X.509 cert version of the key|
|`JWK_META_ALG`|Optional overall crypto algorithm the key is intended for use with|
`lws_jwk_destroy()` should be called when the jwk is going out of scope... this
takes care to zero down any key element data in the jwk.

View file

@ -1,6 +1,12 @@
Changelog
---------
- CHANGE: REMOVED: LWS_WITH_GENRSA, LWS_WITH_GENHASH, LWS_WITH_GENEC,
LWS_WITH_GENAES have all been removed and combined into LWS_WITH_GENCRYPTO
- CHANGE: REMOVED: LWS_WITH_JWS, LWS_WITH_JWE have been removed and combined
into LWS_WITH_JOSE
v3.1.0
======

View file

@ -23,7 +23,10 @@
#cmakedefine LWS_HAS_INTPTR_T
#cmakedefine LWS_HAVE__ATOI64
#cmakedefine LWS_HAVE_ATOLL
#cmakedefine LWS_HAVE_BN_bn2binpad
#cmakedefine LWS_HAVE_ECDSA_SIG_set0
#cmakedefine LWS_HAVE_EVP_MD_CTX_free
#cmakedefine LWS_HAVE_EVP_aes_128_wrap
#cmakedefine LWS_HAVE_LIBCAP
#cmakedefine LWS_HAVE_MALLOC_H
#cmakedefine LWS_HAVE_NEW_UV_VERSION_H
@ -71,17 +74,13 @@
#cmakedefine LWS_WITH_CGI
#cmakedefine LWS_WITH_ESP32
#cmakedefine LWS_WITH_FTS
#cmakedefine LWS_WITH_GENRSA
#cmakedefine LWS_WITH_GENHASH
#cmakedefine LWS_WITH_GENAES
#cmakedefine LWS_WITH_GENEC
#cmakedefine LWS_WITH_GENCRYPTO
#cmakedefine LWS_WITH_HTTP2
#cmakedefine LWS_WITH_HTTP_BROTLI
#cmakedefine LWS_WITH_HTTP_PROXY
#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION
#cmakedefine LWS_WITH_IPV6
#cmakedefine LWS_WITH_JWE
#cmakedefine LWS_WITH_JWS
#cmakedefine LWS_WITH_JOSE
#cmakedefine LWS_WITH_LEJP
#cmakedefine LWS_WITH_LIBEV
#cmakedefine LWS_WITH_LIBEVENT

BIN
doc-assets/lws-overview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 KiB

View file

@ -1,335 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="142.43mm" height="132.94mm" version="1.1" viewBox="0 0 142.42645 132.93726" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="az" x="-.042506" y="-.041505" width="1.085" height="1.083" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.21922556"/>
</filter>
<filter id="ap" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="ag" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="ah" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="ak" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="ao" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="al" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="ai" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="aj" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="aq" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<radialGradient id="c" cx="15.529" cy="71.842" r="31.048" gradientTransform="matrix(.87664 .41986 -.43219 .90238 62.364 -14.24)" gradientUnits="userSpaceOnUse">
<stop offset="0"/>
<stop stop-opacity=".99216" offset=".54286"/>
<stop stop-color="#fffafa" stop-opacity="0" offset="1"/>
</radialGradient>
<filter id="ax" x="-.03" y="-.03" width="1.06" height="1.06" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.60135641"/>
</filter>
<linearGradient id="af" x1="44.768" x2="11.092" y1="82.65" y2="55.121" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#447821" offset="0"/>
<stop stop-color="#112b00" offset="1"/>
</linearGradient>
<filter id="ay" x="-.03" y="-.03" width="1.06" height="1.06" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.35814872"/>
</filter>
<linearGradient id="ae" x1="30.049" x2="15.969" y1="69.08" y2="55.473" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#917c6f" offset="0"/>
<stop stop-color="#483e37" offset="1"/>
</linearGradient>
<filter id="aw" x="-.03" y="-.03" width="1.06" height="1.06" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.18156297"/>
</filter>
<linearGradient id="ad" x1="14.08" x2="22.206" y1="54.906" y2="62.088" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#917c6f" offset="0"/>
<stop stop-color="#c8b7b7" offset="1"/>
</linearGradient>
<path id="h" d="m27.596 49.575c1.8633 3.3048 5.5925 4.7703 9.7554 3.1404" fill="none"/>
<filter id="av" x="-.0336" y="-.0336" width="1.0672" height="1.0672" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.063969468"/>
</filter>
<filter id="at" x="-.0312" y="-.0312" width="1.0624" height="1.0624" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.05940022"/>
</filter>
<filter id="au" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="ar" x="-.0468" y="-.0468" width="1.0936" height="1.0936" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.089100331"/>
</filter>
<filter id="as" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<path id="j" d="m59.668 35.811c3.8865 2.9737 3.4332 2.9031 6.7486 6.8822" fill="none"/>
<path id="i" d="m44.033 47.972c3.8865 2.9737 3.4332 2.9031 6.7486 6.8822" fill="none"/>
<path id="g" d="m26.88 58.809c5.249 5.4788 9.83 5.7421 15.639 4.5358" fill="none"/>
<path id="f" d="m28.723 73.22c5.1014 4.5559 13.146 7.2579 20.222 5.0554" fill="none"/>
<path id="e" d="m53.387 84.721c4.7064-0.87913 10.462-4.6776 13.831-9.7554" fill="none"/>
<path id="d" d="m12.328 58.62c0.62888 6.7681 3.7381 14.383 7.9375 18.899" fill="none"/>
<linearGradient id="ac" x1="32.039" x2="39.021" y1="52.549" y2="37.782" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#2b2200" offset="0"/>
<stop stop-color="#bbe1da" offset="1"/>
</linearGradient>
<linearGradient id="v" x1="28.665" x2="32.34" y1="46.368" y2="51.914" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#bbe1c4" offset="0"/>
<stop stop-opacity=".89669" offset="1"/>
</linearGradient>
<linearGradient id="w" x1="25.691" x2="35.647" y1="49.559" y2="49.559" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="ab" x1="29.097" x2="33.556" y1="47.188" y2="49.894" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#bbe119" offset="0"/>
<stop stop-color="#bbe1da" stop-opacity=".6281" offset="1"/>
</linearGradient>
<linearGradient id="x" x1="38.62" x2="34.645" y1="46.034" y2="42.693" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#2b2242" offset="0"/>
<stop stop-color="#000106" offset="1"/>
</linearGradient>
<linearGradient id="u" x1="31.371" x2="35.714" y1="45.065" y2="47.47" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#2b2200" offset="0"/>
<stop stop-color="#bbe1da" stop-opacity=".62397" offset="1"/>
</linearGradient>
<linearGradient id="t" x1="32.139" x2="38.554" y1="44.33" y2="46.067" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#353939" offset="0"/>
<stop stop-opacity=".79339" offset="1"/>
</linearGradient>
<linearGradient id="aa" x1="33.141" x2="35.881" y1="44.664" y2="46.435" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#e7ff00" offset="0"/>
<stop stop-color="#bbe1da" offset="1"/>
</linearGradient>
<linearGradient id="z" x1="36.75" x2="39.957" y1="41.29" y2="43.963" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="b">
<stop stop-color="#e7ff00" offset="0"/>
<stop stop-color="#e7ff00" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="y" x1="40.09" x2="43.164" y1="38.25" y2="39.853" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
<linearGradient id="s" x1="32.707" x2="37.585" y1="43.862" y2="46.702" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-opacity=".54132" offset="0"/>
<stop stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="r" x1="28.644" x2="32.695" y1="46.307" y2="51.528" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#02290d" stop-opacity=".8843" offset="0"/>
<stop stop-color="#bbe1da" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="q" x1="-2.9766" x2="-1.5828" y1="51.764" y2="53.276" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#aabfcc" stop-opacity=".43388" offset="0"/>
<stop stop-color="#e7e0e6" stop-opacity=".71488" offset="1"/>
</linearGradient>
<linearGradient id="p" x1=".70871" x2="-1.5119" y1="55.095" y2="53.394" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse">
<stop stop-color="#323536" stop-opacity=".34298" offset="0"/>
<stop stop-color="#c0ccd9" stop-opacity=".69421" offset="1"/>
</linearGradient>
<linearGradient id="o" x1=".66146" x2="6.2366" y1="56.678" y2="56.678" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="a">
<stop stop-color="#aabfcc" offset="0"/>
<stop stop-color="#aabfcc" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="n" x1="4.394" x2="8.5517" y1="57.812" y2="57.812" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<linearGradient id="m" x1="6.9453" x2="11.009" y1="58.969" y2="58.969" gradientTransform="translate(15.635 -12.161)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
<path id="l" d="m73.299 43.027c2.6443 3.103 3.6647 8.9348 3.0736 13.029" fill="none"/>
<path id="k" d="m76.3 58.526c-0.31095 4.8023-1.1169 8.064-2.3623 11.717" fill="none"/>
<filter id="an" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
<filter id="am" x="-.0444" y="-.0444" width="1.0888" height="1.0888" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="0.084531083"/>
</filter>
</defs>
<g transform="translate(-9.7869 -105.7)">
<g transform="matrix(1.9303 0 0 1.9303 -.23762 69.525)">
<path d="m72.947 43.64-8.2352 3.8754 2.6226 8.2854 9.5785 0.51567 0.17683-6.5292-2.5391-6.0804z" filter="url(#az)"/>
<path d="m72.43 43.094-8.2352 3.8754 2.6226 8.2854 9.5785 0.51567 0.17683-6.5292-2.5391-6.0804z" fill="#c9c6e7"/>
<path d="m73.702 55.738 2.5041 2.5041" fill="none" stroke="#5956b4" stroke-width=".26458px"/>
<g fill="#2b1100">
<circle cx="16.036" cy="55.906" r="2.2846" filter="url(#ap)"/>
<circle cx="16.579" cy="50.051" r="2.2846" filter="url(#ag)"/>
<circle cx="41.051" cy="29.188" r="2.2846" filter="url(#ah)"/>
<circle cx="36.173" cy="30.123" r="2.2846" filter="url(#ak)"/>
<circle cx="31.496" cy="31.86" r="2.2846" filter="url(#ao)"/>
<circle cx="49.122" cy="84.961" r="2.2846" filter="url(#al)"/>
<circle cx="43.96" cy="85.126" r="2.2846" filter="url(#ai)"/>
<circle cx="38.916" cy="84.406" r="2.2846" filter="url(#aj)"/>
<circle cx="33.849" cy="83" r="2.2846" filter="url(#aq)"/>
</g>
<circle cx="44.031" cy="56.4" r="31.048" fill="url(#c)"/>
<circle cx="44.55" cy="57.439" r="24.054" fill="#280b0b" filter="url(#ax)" stroke="#18161a" stroke-width=".965"/>
<circle cx="43.966" cy="56.925" r="25.792" fill="none"/>
<g>
<circle cx="43.899" cy="56.792" r="24.054" fill="url(#af)" stroke="#339a5b" stroke-width=".465"/>
<circle cx="39.731" cy="52.384" r="14.326" fill="#280b0b" filter="url(#ay)"/>
<circle cx="39.637" cy="52.195" r="14.326" fill="#483737"/>
<circle cx="38.692" cy="51.722" r="14.326" fill="#6c5353"/>
<circle cx="37.512" cy="51.072" r="14.326" fill="url(#ae)"/>
</g>
<path d="m30.244 38.788c7.1148 3.3747 21.503 10.699 21.503 10.699s-0.53647-7.697-8.1364-11.415c-4.5108-1.9133-9.6351-1.7151-13.366 0.7167z" fill="#800000" fill-opacity=".28099"/>
<g>
<circle cx="35.668" cy="48.557" r="7.2625" fill="#280b0b" filter="url(#aw)"/>
<circle cx="35.574" cy="48.321" r="7.2625" fill="#916f6f"/>
<circle cx="34.629" cy="47.565" r="7.2625" fill="#ac9393"/>
<circle cx="33.613" cy="47.163" r="7.2625" fill="url(#ad)"/>
</g>
<path d="m30.736 75.701c6.8963 4.7405 18.012 5.4861 24.656-0.3341" fill="none"/>
<text fill="#1f241c" font-family="'Open Sans Condensed'" font-size="2.9858px" letter-spacing="0px" stroke-width=".37323" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#h"><tspan fill="#1f241c" font-family="'Open Sans Condensed'" stroke-width=".37323">struct lws</tspan></textPath></text>
<path d="m29.857 40.903c3.6364 1.7248 10.99 5.4681 10.99 5.4681s-0.2742-3.934-4.1586-5.8344c-2.3055-0.97792-4.9246-0.87659-6.8317 0.36631z" fill="#a00" fill-opacity=".4876"/>
<text transform="rotate(28.489)" x="50.696777" y="20.985275" fill="#ffffff" font-family="'Open Sans Condensed'" font-size="3.4176px" letter-spacing="0px" stroke-width=".42721" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="50.696777" y="20.985275" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".42721">tls</tspan></text>
<text transform="rotate(28.489)" x="53.057144" y="17.30608" fill="#ffffff" fill-opacity=".5124" font-family="'Open Sans Condensed'" font-size="4.0055px" letter-spacing="0px" stroke-width=".50068" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="53.057144" y="17.30608" fill="#ffffff" fill-opacity=".5124" font-family="'Open Sans Condensed'" stroke-width=".50068">tls ctx</tspan></text>
<g>
<circle cx="69.591" cy="45.368" r="2.2846" fill="#04a" filter="url(#av)"/>
<circle cx="71.316" cy="49.88" r="2.2846" fill="#04a" filter="url(#at)"/>
<circle cx="69.457" cy="45.266" r="2.2846" fill="#5f8dd3"/>
<text x="68.064011" y="45.808071" fill="#000000" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="68.064011" y="45.808071" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">http1</tspan></text>
<circle cx="71.161" cy="49.776" r="2.2846" fill="#5f8dd3"/>
<text x="69.767853" y="50.318241" fill="#000000" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="69.767853" y="50.318241" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">http2</tspan></text>
<circle cx="72.19" cy="54.652" r="2.2846" fill="#04a" filter="url(#au)"/>
<circle cx="72.029" cy="54.587" r="2.2846" fill="#5f8dd3"/>
<text x="71.179817" y="55.129093" fill="#000000" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="71.179817" y="55.129093" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">ws</tspan></text>
<circle cx="71.198" cy="64.408" r="2.2846" fill="#04a" filter="url(#ar)"/>
<circle cx="72.119" cy="59.565" r="2.2846" fill="#04a" filter="url(#as)"/>
<circle cx="71.962" cy="59.464" r="2.2846" fill="#5f8dd3"/>
<text x="70.876762" y="59.983143" fill="#000000" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="70.876762" y="59.983143" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">raw</tspan></text>
<circle cx="71.094" cy="64.275" r="2.2846" fill="#5f8dd3"/>
<text x="70.149879" y="64.746742" fill="#000000" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="70.149879" y="64.746742" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">cgi</tspan></text>
</g>
<g fill="#08a">
<circle cx="69.774" cy="67.9" r=".40476"/>
<circle cx="69.323" cy="69.086" r=".40476"/>
<circle cx="68.755" cy="70.222" r=".40476"/>
</g>
<path d="m59.668 35.811c3.8865 2.9737 3.4332 2.9031 6.7486 6.8822" fill="none"/>
<g>
<text transform="translate(.4009 -.46772)" fill="#0055d4" font-family="'Open Sans Condensed'" font-size="3.4176px" letter-spacing="0px" stroke-width=".42721" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#j"><tspan fill="#0055d4" font-family="'Open Sans Condensed'" stroke-width=".42721">lws_role</tspan></textPath></text>
<text transform="rotate(-90 29.678 55.996)" x="-44.194904" y="-46.217644" fill="#008000" font-family="'Open Sans Condensed'" font-size="3.4176px" letter-spacing="0px" stroke-width=".42721" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#i">protocol</textPath></text>
<circle cx="31.337" cy="31.668" r="2.2846" fill="#008000"/>
<circle cx="37.217" cy="48.907" r="2.2846" fill="#5f8dd3" stroke="#000" stroke-width=".125"/>
<text transform="rotate(25.543)" x="53.502361" y="28.587774" fill="#000000" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="53.502361" y="28.587774" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">role</tspan></text>
<circle cx="36.046" cy="29.894" r="2.2846" fill="#008000"/>
<circle cx="40.96" cy="28.902" r="2.2846" fill="#008000"/>
</g>
<g fill="#677821">
<circle transform="rotate(164.42)" cx="-37.837" cy="-40.708" r=".40476"/>
<circle transform="rotate(164.42)" cx="-36.564" cy="-40.198" r=".40476"/>
<circle transform="rotate(164.42)" cx="-35.294" cy="-39.801" r=".40476"/>
</g>
<path d="m27.596 49.575c1.8633 3.3048 5.5925 4.7703 9.7554 3.1404" fill="none"/>
<text fill="#ffeeaa" font-family="'Open Sans Condensed'" font-size="2.9858px" letter-spacing="0px" stroke-width=".37323" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#g"><tspan fill="#ffeeaa" font-family="'Open Sans Condensed'" stroke-width=".37323">struct lws_vhost</tspan></textPath></text>
<text fill="#fff6d5" font-family="'Open Sans Condensed'" font-size="2.9858px" letter-spacing="0px" stroke-width=".37323" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#f"><tspan fill="#fff6d5" font-family="'Open Sans Condensed'" stroke-width=".37323">struct lws_context</tspan></textPath></text>
<path d="m26.88 58.809c5.249 5.4788 9.83 5.7421 15.639 4.5358" fill="none"/>
<path d="m28.723 73.22c5.1014 4.5559 13.146 7.2579 20.222 5.0554" fill="none"/>
<circle cx="30.23" cy="45.491" r="2.2846" fill="#008000" stroke="#000" stroke-width=".125"/>
<text transform="rotate(25.543)" x="45.184494" y="28.32321" fill="#000000" font-family="'Open Sans Condensed'" font-size="1.1975px" letter-spacing="0px" stroke-width=".14968" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="45.184494" y="28.32321" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".14968">protocol</tspan></text>
<g fill="#ff2a2a">
<circle cx="43.799" cy="84.872" r="2.2846"/>
<circle cx="48.977" cy="84.654" r="2.2846"/>
<circle cx="38.754" cy="84.187" r="2.2846"/>
<circle cx="33.743" cy="82.783" r="2.2846"/>
</g>
<text fill="#d40000" font-family="'Open Sans Condensed'" font-size="3.4176px" letter-spacing="0px" stroke-width="1.6146" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#e">event loops<tspan fill="#d40000" font-family="'Open Sans Condensed'"/></textPath></text>
<path d="m53.387 84.721c4.7064-0.87913 10.462-4.6776 13.831-9.7554" fill="none"/>
<g fill="#c83737">
<circle cx="30.034" cy="80.913" r=".40476"/>
<circle cx="28.798" cy="80.278" r=".40476"/>
<circle cx="27.629" cy="79.576" r=".40476"/>
</g>
<g fill="#000000" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" word-spacing="0px">
<text x="32.110046" y="83.315392" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="32.110046" y="83.315392" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">event</tspan></text>
<text x="37.968678" y="84.685555" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="37.968678" y="84.685555" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">uv</tspan></text>
<text x="47.890556" y="85.158028" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="47.890556" y="85.158028" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">poll</tspan></text>
<text x="43.118603" y="85.299767" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="43.118603" y="85.299767" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20375">ev</tspan></text>
</g>
<text transform="translate(2.3624 1.4174)" fill="#483e37" font-family="'Open Sans Condensed'" font-size="3.4176px" letter-spacing="0px" stroke-width=".42721" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#d">TLS backend</textPath></text>
<path d="m12.328 58.62c0.62888 6.7681 3.7381 14.383 7.9375 18.899" fill="none"/>
<g>
<circle cx="15.919" cy="55.691" r="2.2846" fill="#483e37"/>
<circle cx="16.486" cy="49.833" r="2.2846" fill="#483e37"/>
<text x="16.4466" y="49.484085" fill="#ffffff" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" text-align="center" text-anchor="middle" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="16.4466" y="49.484085" text-align="center" style="line-height:1">open</tspan><tspan x="16.4466" y="51.114063" text-align="center" style="line-height:1">SSL</tspan></text>
<text x="15.812221" y="55.505154" fill="#ffffff" font-family="'Open Sans Condensed'" font-size="1.63px" letter-spacing="0px" stroke-width=".20375" text-align="center" text-anchor="middle" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="15.812221" y="55.505154" text-align="center" style="line-height:1">mbed</tspan><tspan x="15.812221" y="57.135132" text-align="center" style="line-height:1">TLS</tspan></text>
</g>
<path d="m50.315 42.462 2.1734-4.0632 0.85045-3.1183-0.56697-0.47247-3.0238 1.9844-1.8899 3.7798 1.6064 1.0394z" fill-opacity=".30579"/>
<g>
<path d="m41.314 37.17 2.6695-3.4018 2.5513-1.6064 2.2679-0.75595 1.5119-1.0394 4.3467-5.3862 2.9448 0.95507-0.16022 1.3153 1.6653-0.82716 2.5426 0.82464-4.2522 5.6696-1.6064 0.94494-2.5513 1.0394-1.8899 1.5119-1.0394 1.4174-2.3624 3.3073-0.76182-0.73407-0.93907-0.77783-1.2258-0.81128-1.3255-0.70062-1.1593-0.52777z" fill="url(#ac)" stroke="#000" stroke-width=".26458px"/>
<path d="m51.282 36.412-3.2406 4.744-1.0023-0.86863-1.2027-1.0023-0.90204-0.60136-1.3363-0.66817-1.0357-0.4009-1.2361-0.4009 2.7729-3.5747z" fill="url(#v)" stroke="url(#w)" stroke-width=".26458px"/>
<path d="m44.133 38.216-0.06682-4.1761 1.9377 0.96885-0.3675 3.1738 2.6393-2.205 2.1382 1.1693-4.2429 2.205-0.83522-0.50113z" fill="url(#ab)"/>
<path d="m50.347 30.298 6.9824 2.6059-1.7373 1.0691-6.8822-2.6059z" fill="url(#x)"/>
<path d="m53.354 34.842-2.1047 1.637-7.1495-2.8731 2.5057-1.47z" fill="url(#u)"/>
<path d="m55.625 33.907-6.7151-2.5057-2.372 0.7684 6.782 2.7395z" fill="url(#t)"/>
<path d="m47.808 34.875 0.03341-1.9377 0.60136-1.3363 1.8709 0.80181-0.66817 0.93544-0.13363 0.83522 0.96885-0.3675 1.1693-0.70158 2.7395 1.1693-2.372 0.50113-2.4388 0.26727z" fill="url(#aa)"/>
<path d="m54.657 24.92 3.0902 1.0442-0.13775 1.1403 1.2343-0.76322 2.8624 0.98418-4.3097 5.5792-7.0492-2.5391z"/>
<path d="m51.984 31.367-0.16704-0.50113 0.3675-2.6059 2.0045 0.73499-0.56795 1.9377 3.0402-1.2361 2.2384 0.66817-3.7084 1.7373z" fill="url(#z)"/>
<path d="m56.026 28.294-0.86862-3.0068 2.1716 0.66817-0.13364 1.637 1.8375-1.0023 2.205 0.66817-5.2118 1.2027z" fill="url(#y)"/>
<path d="m46.572 32.136 6.9156 2.7395 2.205-0.83522-6.8154-2.7061z" fill="url(#s)"/>
<path d="m44.066 33.65-2.7758 3.5435 1.2048 0.38979 1.2166 0.53153 0.76776 0.48428 1.2048 0.59059 0.81501 0.57878 0.75595 0.73233 0.74414 0.67327 3.2837-4.5711-2.6104-1.1576-2.764-1.0158z" fill="url(#r)"/>
<text transform="rotate(18.076)" x="63.807125" y="1.2534895" fill="#000000" font-family="'Open Sans Condensed'" font-size="2.1167px" letter-spacing="0px" stroke-width=".26458" text-align="center" text-anchor="middle" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="63.807125" y="1.2534895" text-align="center" style="line-height:1">tcp, udp,</tspan><tspan x="63.807125" y="3.370156" text-align="center" style="line-height:1">unix listen</tspan><tspan x="63.807125" y="5.4868226" text-align="center" style="line-height:1">socket(s)</tspan></text>
</g>
<g stroke="#000" stroke-width=".26458px">
<path d="m26.66 45.165-2.7395-0.46772-1.9377-1.2695-3.5413-1.0691-3.0946-2.2914-3.0526-0.71542 0.53454 3.2072 3.2741 2.8063 3.8754 1.1359 2.94 1.6036 3.5413 0.80181-0.06682-1.9377z" fill="#2b2200"/>
<path d="m26.526 45.8-1.6036 0.63476 1.5368 1.4366v-1.2027z" fill="#a80"/>
<path d="m23.987 44.831-1.7373-0.45102-2.2718 0.21716 0.70158 0.50113 2.0045 2.7061 1.6203 0.48443-1.6537-2.4221 2.9734-0.75169z" fill="#d4aa00"/>
<path d="m20.012 42.994-2.372-0.23386-1.8041-0.30068 0.56795 2.1716 2.0045 1.3029 1.3698 0.53454-1.9711-2.6393 4.0759-0.50113-1.7038-0.33409z" fill="#fc0"/>
<path d="m15.168 44.497-0.63476-3.1738 3.3075 0.56795-1.6661-1.2581-3.2784-0.67964 0.93544 3.5079z" fill="#ffe680"/>
</g>
<g>
<path d="m12.709 42.576 2.7122-2.5652-3.2569-0.78101z" fill="url(#q)"/>
<path d="m15.399 40.005-2.6931 2.5513 3.5908 3.0238 2.3151-3.3073z" fill="url(#p)"/>
<path d="m18.659 42.368 3.2128 0.99219-1.8426 3.3073-3.7325-1.3229z" fill="url(#o)"/>
<path d="m21.825 43.36 2.3624 1.3702-1.5592 3.2128-2.5986-1.3702z" fill="url(#n)"/>
<path d="m24.281 44.635 2.3624 0.61421-0.10564 0.6713-0.08335 0.60437-0.01957 0.9022-0.09187 1.0537 0.15869 0.50099-3.9215-0.99219z" fill="url(#m)"/>
<text transform="rotate(-48.141)" x="-21.20005" y="30.030567" fill="#000000" font-family="'Open Sans Condensed'" font-size="2.1167px" letter-spacing="0px" stroke-width=".26458" text-align="center" text-anchor="middle" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="-21.20005" y="30.030567" text-align="center" style="line-height:1">tcp, udp,</tspan><tspan x="-21.20005" y="32.147232" text-align="center" style="line-height:1">http1,h2, ws</tspan><tspan x="-21.20005" y="34.263901" text-align="center" style="line-height:1">tls client(s)</tspan></text>
<path d="m72.143 42.462 1.5119 2.7403 0.88562 2.0172 0.68164 2.1911 0.37274 2.0367 0.13887 1.787-0.0945 3.0711 3.3383 0.20855-0.05374-1.0854 0.05612-1.2574-0.03536-2.4677-0.32879-2.8159-0.67926-2.5875-1.3051-2.9876-1.6789-2.5051z" fill="#e7e0e6"/>
</g>
<g fill="#050">
<circle cx="31.304" cy="31.668" r="1.6499"/>
<circle cx="36.048" cy="29.864" r="1.6499"/>
<circle cx="40.959" cy="28.862" r="1.6499"/>
</g>
<text transform="rotate(33.374)" x="71.540237" y="5.0364399" fill="#ffffff" font-family="'Open Sans Condensed'" font-size="3.2488px" letter-spacing="0px" stroke-width=".40609" text-align="center" text-anchor="middle" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="71.540237" y="5.0364399" text-align="center" style="line-height:1">ALPN</tspan><tspan x="71.540237" y="8.2851915" text-align="center" style="line-height:1">SNI</tspan></text>
<text transform="translate(.26727 .46772)" fill="#000000" font-family="'Open Sans Condensed'" font-size="2.417px" letter-spacing="0px" stroke-width=".30212" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#l"><tspan font-family="'Open Sans Condensed'" stroke-width=".30212">ah http headers</tspan></textPath></text>
<path d="m73.299 43.027c2.6443 3.103 3.6647 8.9348 3.0736 13.029" fill="none"/>
<g>
<circle cx="55.247" cy="64.799" r="7.6227" fill="#4b4046"/>
<circle cx="55.035" cy="63.845" r="7.6227" fill="#6f5b55"/>
<circle cx="54.369" cy="63.049" r="7.6227" fill="#ac9393"/>
<g font-family="'Open Sans Condensed'" letter-spacing="0px" text-anchor="middle" word-spacing="0px">
<text transform="rotate(26.555)" x="76.934166" y="31.127186" fill="#000000" font-size="2.5688px" stroke-width=".3211" text-align="center" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="76.934166" y="31.127186" font-family="'Open Sans Condensed'" stroke-width=".3211" text-align="center" text-anchor="middle" style="line-height:1">event loop</tspan></text>
<text transform="rotate(29.003)" x="75.430794" y="40.665337" fill="#cccccc" font-size="2.5688px" stroke-width=".3211" text-align="center" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="75.430794" y="40.665337" text-align="center" style="line-height:1">peer tracking</tspan><tspan x="75.430794" y="43.234123" text-align="center" style="line-height:1">basic auth</tspan></text>
<text transform="rotate(38.032)" x="61.58363" y="36.673119" fill="#cccccc" font-size="2.4588px" stroke-width=".30735" text-align="center" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="61.58363" y="36.673119" fill="#cccccc" font-family="'Open Sans Condensed'" stroke-width=".30735" text-align="center" text-anchor="middle" style="line-height:1">logging</tspan></text>
</g>
</g>
<path d="m76.3 58.526c-0.31095 4.8023-1.1169 8.064-2.3623 11.717" fill="none"/>
<g>
<text fill="#000000" font-family="'Open Sans Condensed'" font-size="2.2084px" letter-spacing="0px" stroke-width=".27605" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><textPath xlink:href="#k"><tspan font-family="'Open Sans Condensed'" stroke-width=".27605">ws ext: pm-def</tspan></textPath></text>
<circle cx="32.072" cy="26.49" r="2.2846" fill="#2b1100" filter="url(#an)"/>
<circle cx="31.914" cy="26.298" r="2.2846" fill="#008000"/>
<circle cx="31.88" cy="26.298" r="1.6499" fill="#050"/>
<circle cx="37.618" cy="24.986" r="2.2846" fill="#2b1100" filter="url(#am)"/>
<circle cx="37.459" cy="24.794" r="2.2846" fill="#008000"/>
<circle cx="37.426" cy="24.794" r="1.6499" fill="#050"/>
<g font-family="'Open Sans Condensed'" letter-spacing="0px" text-anchor="middle" word-spacing="0px">
<text transform="rotate(-21.452)" x="20.092173" y="36.830799" fill="#ffffff" font-size="2.1167px" stroke-width=".26458" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="20.092173" y="36.830799" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".26458">ssh</tspan></text>
<text transform="rotate(-15.946)" x="29.145987" y="34.664425" fill="#ffffff" font-size="1.6275px" stroke-width=".20343" text-align="center" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25" xml:space="preserve"><tspan x="29.145987" y="34.664425" fill="#ffffff" font-family="'Open Sans Condensed'" stroke-width=".20343">ACME</tspan></text>
<text transform="rotate(32.893)" x="79.83622" y="9.3089314" fill="#cccccc" font-size="2.5688px" stroke-width=".3211" text-align="center" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="79.83622" y="9.3089314" text-align="center" style="line-height:1">lws_ring</tspan><tspan x="79.83622" y="11.877718" text-align="center" style="line-height:1">VFS</tspan><tspan x="79.83622" y="14.446506" text-align="center" style="line-height:1">zip_fops</tspan></text>
</g>
</g>
<path d="m60.073 68.304c-5.1565-2.4458-13.364-6.5727-13.364-6.5727s-1.1231 5.484 4.385 8.1788c3.2693 1.3867 5.991 0.86504 8.9787-1.6061z" fill="#06f" fill-opacity=".4876"/>
<text transform="rotate(26.859)" x="77.195198" y="36.191971" fill="#e9ddaf" font-family="'Open Sans Condensed'" font-size="1.8247px" letter-spacing="0px" stroke-width=".22809" text-align="center" text-anchor="middle" word-spacing="0px" style="font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1" xml:space="preserve"><tspan x="77.195198" y="36.191971" fill="#e9ddaf" font-family="'Open Sans Condensed'" stroke-width=".22809" text-align="center" text-anchor="middle" style="line-height:1">service thread(s)</tspan></text>
<g transform="matrix(2.4352 0 0 2.4352 154.23 -92.854)">
<path d="m-53.889 51.059c-0.2293-0.20876-0.45859-0.41752-0.68788-0.62628h-2.0842c-0.41073-0.4478-0.80598-0.91066-1.2269-1.3486-0.15635-0.18661-0.4411-0.26749-0.65538-0.13157-0.2532 0.13273-0.37597 0.41669-0.39448 0.69074-0.04758 0.31909 0.05354 0.68674 0.35175 0.85254 0.18832 0.10808 0.49386 0.04671 0.5437-0.19062 0.06699-0.21357-0.06488-0.51518-0.31343-0.50948-0.16072 3e-3 -0.26391 0.28316-0.07717 0.33226 0.08893 0.01617 0.10978-0.27326 0.17002-0.07939 0.0638 0.17507-0.16528 0.31938-0.28293 0.16402-0.213-0.21052-0.14192-0.60391 0.10406-0.75718 0.23529-0.07437 0.39199 0.16171 0.52824 0.31112 0.3873 0.42849 0.77336 0.85822 1.1601 1.2873 0.95481 0.0017 1.9097 0.0034 2.8645 0.0051z"/>
<path d="m-56.225 50.077c0.07138-0.08712 0.14278-0.17424 0.21416-0.26135 0.10648 0.11858 0.21295 0.23715 0.31943 0.35573 0.19118 0.0012 0.38235 0.0025 0.57352 0.0036-0.2069-0.22868-0.41381-0.45737-0.62072-0.68605 0.10043-0.11374 0.20086-0.22747 0.30128-0.34121 0.19601 0.22868 0.39203 0.45737 0.58804 0.68605-0.0012-0.219-0.0025-0.43801-0.0036-0.65701-0.10043-0.11252-0.20085-0.22505-0.30128-0.33758 0.07198-0.10615 0.24907-0.21551 0.08668-0.31451-0.36406-0.40214-0.72811-0.80428-1.0922-1.2064-0.76543-0.0026-1.5317 0.01021-2.2966-0.0021-0.1821-0.01604-0.40898-0.07372-0.45071-0.28286-0.088-0.27195 0.21101-0.59285 0.48564-0.46528 0.18982 0.03674 0.11004 0.42003-0.05779 0.29297 0.17429-0.25602-0.31955-0.22697-0.17976 0.02451 0.09575 0.22004 0.44485 0.25635 0.58255 0.05617 0.14323-0.2308-0.05154-0.4937-0.2705-0.58497-0.21915-0.10466-0.49117-0.07771-0.67434 0.08567-0.27378 0.20864-0.40621 0.61643-0.22752 0.92795 0.104 0.233 0.33446 0.38219 0.58455 0.41121 0.33507 0.04187 0.67431 0.01608 1.0114 0.02326h1.3407c0.25409 0.28192 0.50818 0.56384 0.76228 0.84577-0.22868 0.25288-0.45736 0.50576-0.68605 0.75865-0.16572-0.21381-0.41577-0.37628-0.51666-0.63121-0.08161-0.29871 0.35413-0.53156 0.5618-0.30555 0.19943 0.11035-0.01178 0.46291-0.15577 0.26978 0.1166-0.03951 0.16904-0.22582-0.01437-0.21638-0.21499 0.08183-0.12303 0.40655 0.0643 0.46926 0.17871 0.08651 0.40261-0.07556 0.3799-0.27118 0.01826-0.28278-0.18842-0.5907-0.48713-0.60388-0.31967-0.06227-0.68039 0.12472-0.75854 0.45263-0.10766 0.28228 0.06955 0.56541 0.26324 0.75966 0.22723 0.24585 0.44911 0.49691 0.67408 0.74475z" fill="#f00"/>
<path d="m-54.464 50.361c0.0017-0.54757 0.0034-1.0951 0.0051-1.6427-0.33832-0.38748-0.69457-0.75998-1.021-1.1574-0.10913-0.13496-0.19252-0.29467-0.18614-0.47289-0.0086-0.33136 0.19731-0.65612 0.50556-0.78212 0.23741-0.10513 0.52154-0.11525 0.754 0.0094 0.3243 0.16516 0.47791 0.61109 0.30157 0.93353-0.13519 0.19951-0.48216 0.19542-0.59404-0.0265-0.1079-0.14135-0.10151-0.43084 0.10373-0.47103 0.16696-0.0034 0.20764 0.24106 0.02397 0.25083-0.04085 0.15882 0.28306 0.12232 0.27255-0.04546 0.04138-0.23154-0.21514-0.42013-0.4314-0.37653-0.24357 0.02414-0.45758 0.28096-0.37332 0.52553 0.08871 0.24037 0.30285 0.40102 0.46218 0.59282 0.25613 0.27896 0.51148 0.55865 0.76757 0.83766-0.0017 0.61002-0.0034 1.22-0.0051 1.8301-0.19509-0.0017-0.3902-0.0035-0.58527-0.0051z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 37 KiB

View file

@ -367,6 +367,9 @@ typedef unsigned long long lws_filepos_t;
typedef long long lws_fileofs_t;
typedef uint32_t lws_fop_flags_t;
#define lws_concat_temp(_t, _l) (_t + sizeof(_t) - _l)
#define lws_concat_used(_t, _l) (sizeof(_t) - _l)
/** struct lws_pollargs - argument structure for all external poll related calls
* passed in via 'in' */
struct lws_pollargs {
@ -427,8 +430,8 @@ struct lws;
#include <libwebsockets/lws-genaes.h>
#include <libwebsockets/lws-genec.h>
#include <libwebsockets/lws-jose.h>
#include <libwebsockets/lws-jwk.h>
#include <libwebsockets/lws-jose.h>
#include <libwebsockets/lws-jws.h>
#include <libwebsockets/lws-jwe.h>

View file

@ -46,6 +46,7 @@ enum enum_aes_modes {
LWS_GAESM_OFB,
LWS_GAESM_XTS, /* care... requires double-length key */
LWS_GAESM_GCM,
LWS_GAESM_KW,
};
enum enum_aes_operation {
@ -53,6 +54,11 @@ enum enum_aes_operation {
LWS_GAESO_DEC
};
enum enum_aes_padding {
LWS_GAESP_NO_PADDING,
LWS_GAESP_WITH_PADDING
};
/* include/libwebsockets/lws-jwk.h must be included before this */
#define LWS_AES_BLOCKSIZE 128
@ -99,7 +105,7 @@ struct lws_genaes_ctx {
LWS_VISIBLE LWS_EXTERN int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
int padding, void *engine);
enum enum_aes_padding padding, void *engine);
/** lws_genaes_destroy() - Destroy genaes AES context
*
@ -119,21 +125,22 @@ lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen);
/** lws_genaes_crypt() - Encrypt or decrypt
*
* \param ctx: your struct lws_genaes_ctx
* \param in: input plaintext or ciphertext
* \param len: length of input (which is always length of output)
* \param out: output plaintext or ciphertext
* \param op: LWS_GAESO_ENC or LWS_GAESO_DEC
* \param iv_or_nonce_ctr_or_data_unit_16: NULL, iv, nonce_ctr16, or data_unit16
* \param stream_block_16: pointer to 16-byte stream block for CTR mode only
* \param nc_or_iv_off: NULL or pointer to nc, or iv_off
* \param in: input plaintext or ciphertext
* \param len: length of input (which is always length of output)
* \param out: output plaintext or ciphertext
* \param taglen: length of tag
*
* Encrypts or decrypts using the AES mode set when the ctx was created.
* The last three arguments have different meanings depending on the mode:
*
* CBC CFB128 CFB8 CTR ECB OFB XTS
* iv_or_nonce_ctr_or_data_unit_16 : iv iv iv nonce NULL iv dataunt
* stream_block_16 : NULL NULL NULL stream NULL NULL NULL
* nc_or_iv_off : NULL iv_off NULL nc_off NULL iv_off NULL
* KW CBC CFB128 CFB8 CTR ECB OFB XTS
* iv_or_nonce_ct.._unit_16 : iv iv iv iv nonce NULL iv dataunt
* stream_block_16 : NULL NULL NULL NULL stream NULL NULL NULL
* nc_or_iv_off : NULL NULL iv_off NULL nc_off NULL iv_off NULL
*
* For GCM:
*

View file

@ -27,12 +27,12 @@
* no dependency at all on any JOSE type.
*/
enum lws_gencrypto_kyt {
LWS_GENCRYPTO_KYT_UNKNOWN,
enum lws_gencrypto_kty {
LWS_GENCRYPTO_KTY_UNKNOWN,
LWS_GENCRYPTO_KYT_OCT,
LWS_GENCRYPTO_KYT_RSA,
LWS_GENCRYPTO_KYT_EC
LWS_GENCRYPTO_KTY_OCT,
LWS_GENCRYPTO_KTY_RSA,
LWS_GENCRYPTO_KTY_EC
};
/*
@ -92,3 +92,26 @@ struct lws_gencrypto_keyelem {
uint8_t *buf;
uint16_t len;
};
/**
* lws_gencrypto_bits_to_bytes() - returns rounded up bytes needed for bits
*
* \param bits
*
* Returns the number of bytes needed to store the given number of bits. If
* a byte is partially used, the byte count is rounded up.
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_bits_to_bytes(int bits);
/**
* lws_base64_size() - returns estimated size of base64 encoding
*
* \param bytes
*
* Returns a slightly oversize estimate of the size of a base64 encoded version
* of the given amount of unencoded data.
*/
LWS_VISIBLE LWS_EXTERN int
lws_base64_size(int bytes);

View file

@ -143,11 +143,12 @@ LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
struct lws_gencrypto_keyelem *el);
/** lws_genecdsa_hash_sig_verify() - Verifies ECDSA signature on a given hash
/** lws_genecdsa_hash_sig_verify_jws() - Verifies a JWS ECDSA signature on a given hash
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: unencrypted payload (usually a recomputed hash)
* \param hash_type: one of LWS_GENHASH_TYPE_
* \param keybits: number of bits in the crypto key
* \param sig: pointer to the signature we received with the payload
* \param sig_len: length of the signature we are checking in bytes
*
@ -158,31 +159,38 @@ lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
*
* Returns <0 for error, or 0 if signature matches the hash + key..
*
* The JWS ECDSA signature verification algorithm differs to generic ECDSA
* signatures and they're not interoperable.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sig_verify(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
const uint8_t *sig, size_t sig_len);
lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, int keybits,
const uint8_t *sig, size_t sig_len);
/** lws_genecdsa_hash_sign() - Creates an ECDSA signature for a hash you provide
/** lws_genecdsa_hash_sign_jws() - Creates a JWS ECDSA signature for a hash you provide
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: precomputed hash
* \param hash_type: one of LWS_GENHASH_TYPE_
* \param keybits: number of bits in the crypto key
* \param sig: pointer to buffer to take signature
* \param sig_len: length of the buffer (must be >= length of key N)
*
* Returns <0 for error, or 0 for success.
*
* This creates an ECDSA signature for a hash you already computed and provide.
* This creates a JWS ECDSA signature for a hash you already computed and provide.
*
* The JWS ECDSA signature generation algorithm differs to generic ECDSA
* signatures and they're not interoperable.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, uint8_t *sig,
size_t sig_len);
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);
/* Apis that apply to both ECDH and ECDSA */

View file

@ -53,26 +53,14 @@ struct lws_genrsa_ctx {
enum enum_genrsa_mode mode;
};
/** lws_genrsa_destroy_elements() - Free allocations in genrsa_elements
*
* \param el: your struct lws_gencrypto_keyelem
*
* This is a helper for user code making use of struct lws_gencrypto_keyelem
* where the elements are allocated on the heap, it frees any non-NULL
* buf element and sets the buf to NULL.
*
* NB: lws_genrsa_public_... apis do not need this as they take care of the key
* creation and destruction themselves.
*/
LWS_VISIBLE LWS_EXTERN void
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context
*
* \param ctx: your struct lws_genrsa_ctx
* \param el: struct prepared with key element data
* \param context: lws_context for RNG
* \param mode: RSA mode, one of LGRSAM_ constants
* \param oaep_hashid: the lws genhash id for the hash used in MFG1 hash
* used in OAEP mode - normally, SHA1
*
* Creates an RSA context with a public key associated with it, formed from
* the key elements in \p el.
@ -86,7 +74,22 @@ lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
struct lws_context *context, enum enum_genrsa_mode mode);
struct lws_context *context, enum enum_genrsa_mode mode,
enum lws_genhash_types oaep_hashid);
/** lws_genrsa_destroy_elements() - Free allocations in genrsa_elements
*
* \param el: your struct lws_gencrypto_keyelem
*
* This is a helper for user code making use of struct lws_gencrypto_keyelem
* where the elements are allocated on the heap, it frees any non-NULL
* buf element and sets the buf to NULL.
*
* NB: lws_genrsa_public_... apis do not need this as they take care of the key
* creation and destruction themselves.
*/
LWS_VISIBLE LWS_EXTERN void
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el);
/** lws_genrsa_new_keypair() - Create new RSA keypair
*
@ -111,7 +114,7 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el,
int bits);
/** lws_genrsa_public_encrypt() - Perform RSA public encryption
/** lws_genrsa_public_encrypt() - Perform RSA public key encryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: plaintext input
@ -128,7 +131,24 @@ LWS_VISIBLE LWS_EXTERN int
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out);
/** lws_genrsa_public_decrypt() - Perform RSA public decryption
/** lws_genrsa_private_encrypt() - Perform RSA private key encryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: plaintext input
* \param in_len: length of plaintext input
* \param out: encrypted output
*
* Performs PKCS1 v1.5 Encryption
*
* Returns <0 for error, or length of decrypted data.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out);
/** lws_genrsa_public_decrypt() - Perform RSA public key decryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: encrypted input
@ -146,6 +166,24 @@ LWS_VISIBLE LWS_EXTERN int
lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max);
/** lws_genrsa_private_decrypt() - Perform RSA private key decryption
*
* \param ctx: your struct lws_genrsa_ctx
* \param in: encrypted input
* \param in_len: length of encrypted input
* \param out: decrypted output
* \param out_max: size of output buffer
*
* Performs PKCS1 v1.5 Decryption
*
* Returns <0 for error, or length of decrypted data.
*
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
*/
LWS_VISIBLE LWS_EXTERN int
lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max);
/** lws_genrsa_hash_sig_verify() - Verifies RSA signature on a given hash
*
* \param ctx: your struct lws_genrsa_ctx

View file

@ -49,11 +49,6 @@ enum lws_jws_jose_hdr_indexes {
LWS_COUNT_JOSE_HDR_ELEMENTS
};
struct lws_jose {
/* jose header elements */
struct lws_gencrypto_keyelem e[LWS_COUNT_JOSE_HDR_ELEMENTS];
};
enum lws_jose_algtype {
LWS_JOSE_ENCTYPE_NONE,
@ -62,6 +57,7 @@ enum lws_jose_algtype {
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_AES_CBC,
LWS_JOSE_ENCTYPE_AES_CFB128,
@ -69,10 +65,12 @@ enum lws_jose_algtype {
LWS_JOSE_ENCTYPE_AES_CTR,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_AES_OFB,
LWS_JOSE_ENCTYPE_AES_XTS, /* care... requires double-length key */
LWS_JOSE_ENCTYPE_AES_XTS, /* care: requires double-length key */
LWS_JOSE_ENCTYPE_AES_GCM,
};
/* there's a table of these defined in lws-gencrypto-common.c */
struct lws_jose_jwe_alg {
enum lws_genhash_types hash_type;
enum lws_genhmac_types hmac_type;
@ -80,16 +78,97 @@ struct lws_jose_jwe_alg {
enum lws_jose_algtype algtype_crypto; /* the encryption cipher */
const char *alg; /* the JWA enc alg name, eg "ES512" */
const char *curve_name; /* NULL, or, eg, "P-256" */
unsigned short keybits_min, keybits_fixed;
unsigned short ivbits;
};
struct lws_jose {
/* jose header elements */
struct lws_gencrypto_keyelem e[LWS_COUNT_JOSE_HDR_ELEMENTS];
struct lws_jwk jwk_ephemeral;
const struct lws_jose_jwe_alg *alg;
const struct lws_jose_jwe_alg *enc_alg;
};
/**
* lws_jose_init() - prepare a struct lws_jose for use
*
* \param jose: the jose header struct to prepare
*/
LWS_VISIBLE LWS_EXTERN void
lws_jose_init(struct lws_jose *jose);
/**
* lws_jose_destroy() - retire a struct lws_jose from use
*
* \param jose: the jose header struct to destroy
*/
LWS_VISIBLE LWS_EXTERN void
lws_jose_destroy(struct lws_jose *jose);
/**
* lws_gencrypto_jws_alg_to_definition() - look up a jws alg name
*
* \param alg: the jws alg name
* \param jose: pointer to the pointer to the info struct to set on success
*
* Returns 0 if *jose set, else nonzero for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jws_alg_to_definition(const char *alg,
const struct lws_jose_jwe_alg **jose);
/**
* lws_gencrypto_jwe_alg_to_definition() - look up a jwe alg name
*
* \param alg: the jwe alg name
* \param jose: pointer to the pointer to the info struct to set on success
*
* Returns 0 if *jose set, else nonzero for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jwe_alg_to_definition(const char *alg,
const struct lws_jose_jwe_alg **jose);
/**
* lws_gencrypto_jwe_enc_to_definition() - look up a jwe enc name
*
* \param alg: the jwe enc name
* \param jose: pointer to the pointer to the info struct to set on success
*
* Returns 0 if *jose set, else nonzero for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_gencrypto_jwe_enc_to_definition(const char *enc,
const struct lws_jose_jwe_alg **jose);
/**
* lws_jws_parse_jose() - parse a JWS JOSE header
*
* \param jose: the jose struct to set to parsing results
* \param buf: the raw JOSE header
* \param len: the length of the raw JOSE header
* \param temp: parent-owned buffer to "allocate" elements into
* \param temp_len: amount of space available in temp
*
* returns the amount of temp used, or -1 for error
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len);
/**
* lws_jwe_parse_jose() - parse a JWE JOSE header
*
* \param jose: the jose struct to set to parsing results
* \param buf: the raw JOSE header
* \param len: the length of the raw JOSE header
* \param temp: parent-owned buffer to "allocate" elements into
* \param temp_len: amount of space available in temp
*
* returns the amount of temp used, or -1 for error
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len);

View file

@ -19,8 +19,27 @@
* MA 02110-1301 USA
*
* included from libwebsockets.h
*
* JWE Compact Serialization consists of
*
* BASE64URL(UTF8(JWE Protected Header)) || '.' ||
* BASE64URL(JWE Encrypted Key) || '.' ||
* BASE64URL(JWE Initialization Vector) || '.' ||
* BASE64URL(JWE Ciphertext) || '.' ||
* BASE64URL(JWE Authentication Tag)
*/
#define LWS_JWE_RFC3394_OVERHEAD_BYTES 8
#define LWS_JWE_AES_IV_BYTES 16
#define LWS_JWE_LIMIT_RSA_KEY_BITS 4096
#define LWS_JWE_LIMIT_AES_KEY_BITS (512 + 64) /* RFC3394 Key Wrap adds 64b */
#define LWS_JWE_LIMIT_EC_KEY_BITS 528 /* 521 rounded to byte boundary */
#define LWS_JWE_LIMIT_HASH_BITS (LWS_GENHASH_LARGEST * 8)
/* the largest key element for any cipher */
#define LWS_JWE_LIMIT_KEY_ELEMENT_BYTES (LWS_JWE_LIMIT_RSA_KEY_BITS / 8)
/**
* lws_jwe_create_packet() - add b64 sig to b64 hdr + payload
*
@ -43,7 +62,78 @@
* Returns the length written to \p out, or -1.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_create_packet(struct lws_jwk *jwk,
const struct lws_jose_jwe_alg *jose_alg,
lws_jwe_create_packet(struct lws_jose *jose, struct lws_jwk *jwk,
const char *payload, size_t len, const char *nonce,
char *out, size_t out_len, struct lws_context *context);
LWS_VISIBLE LWS_EXTERN void
lws_jwe_be64(uint64_t c, uint8_t *p8);
/*
* JWE Compact Serialization consists of
*
* BASE64URL(UTF8(JWE Protected Header)) || '.' ||
* BASE64URL(JWE Encrypted Key) || '.' ||
* BASE64URL(JWE Initialization Vector) || '.' ||
* BASE64URL(JWE Ciphertext) || '.' ||
* BASE64URL(JWE Authentication Tag)
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_write_compact(struct lws_jose *jose, struct lws_jws *jws,
char *out, size_t out_len);
/**
* lws_jwe_auth_and_decrypt() - confirm and decrypt JWE
*
* \param jose: jose context
* \param jws: jws / jwe context... .map and .map_b64 must be filled already
*
* This is a high level JWE decrypt api that takes a jws with the maps
* already processed, and if the authentication passes, returns the decrypted
* plaintext in jws.map.buf[LJWE_CTXT] and its length in jws.map.len[LJWE_CTXT].
*
* In the jws, the following fields must have been set by the caller
*
* .context
* .jwk (the key encryption key)
* .map
* .map_b64
*
* Having the b64 and decoded maps filled externally makes it flexible where
* the data was picked from, eg, from a Complete JWE JSON serialization, a
* flattened one, or a Compact Serialization.
*
* Returns decrypt length, or -1 for failure.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_auth_and_decrypt(struct lws_jose *jose, struct lws_jws *jws);
/* only exposed because we have test vectors that need it */
LWS_VISIBLE LWS_EXTERN int
lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jose *jose,
struct lws_jws *jws, uint8_t *enc_cek,
uint8_t *aad, int aad_len);
/* only exposed because we have test vectors that need it */
LWS_VISIBLE LWS_EXTERN int
lws_jwa_concat_kdf(struct lws_jose *jose, struct lws_jws *jws, int direct,
uint8_t *out, const uint8_t *shared_secret, int sslen);
/**
* lws_jwe_encrypt() - perform JWE encryption
*
* \param jose: the JOSE header information (encryption types, etc)
* \param jws: the JWE elements, pointer to jwk etc
* \param temp: parent-owned buffer to "allocate" elements into
* \param temp_len: amount of space available in temp
*
* returns the amount of temp used, or -1 for error
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwe_encrypt(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len);

View file

@ -52,13 +52,23 @@ struct lws_jwk {
/* generic meta key elements, like KID */
struct lws_gencrypto_keyelem meta[LWS_COUNT_JWK_ELEMENTS];
int kty; /**< one of LWS_JWK_ */
char private_key; /* nonzero = has private key elements */
};
typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
struct lws_jwk_parse_state {
struct lws_jwk *jwk;
char b64[(((8192 / 8) * 4) / 3) + 1]; /* enough for 8Kb key */
lws_jwk_key_import_callback per_key_cb;
void *user;
int pos;
unsigned short possible;
};
/** lws_jwk_import() - Create a JSON Web key from the textual representation
*
* \param s: the JWK object to create
* \param jwk: the JWK object to create
* \param cb: callback for each jwk-processed key, or NULL if importing a single
* key with no parent "keys" JSON
* \param user: pointer to be passed to the callback, otherwise ignored by lws.
@ -80,21 +90,21 @@ typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
* iteration through any further keys).
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_import(struct lws_jwk *s, lws_jwk_key_import_callback cb, void *user,
lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user,
const char *in, size_t len);
/** lws_jwk_destroy() - Destroy a JSON Web key
*
* \param s: the JWK object to destroy
* \param jwk: the JWK object to destroy
*
* All allocations in the lws_jwk are destroyed
*/
LWS_VISIBLE LWS_EXTERN void
lws_jwk_destroy(struct lws_jwk *s);
lws_jwk_destroy(struct lws_jwk *jwk);
/** lws_jwk_export() - Export a JSON Web key to a textual representation
*
* \param s: the JWK object to export
* \param jwk: the JWK object to export
* \param _private: 0 = just export public parts, 1 = export everything
* \param p: the buffer to write the exported JWK to
* \param len: the length of the buffer \p p in bytes
@ -104,11 +114,11 @@ lws_jwk_destroy(struct lws_jwk *s);
* Serializes the content of the JWK into a char buffer.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_export(struct lws_jwk *s, int _private, char *p, size_t len);
lws_jwk_export(struct lws_jwk *jwk, int _private, char *p, size_t len);
/** lws_jwk_load() - Import a JSON Web key from a file
*
* \param s: the JWK object to load into
* \param jwk: the JWK object to load into
* \param filename: filename to load from
*
* Returns 0 for OK or -1 for failure
@ -125,29 +135,58 @@ lws_jwk_export(struct lws_jwk *s, int _private, char *p, size_t len);
* iteration through any further keys, leaving the last one in s).
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_load(struct lws_jwk *s, const char *filename,
lws_jwk_load(struct lws_jwk *jwk, const char *filename,
lws_jwk_key_import_callback cb, void *user);
/** lws_jwk_save() - Export a JSON Web key to a file
*
* \param s: the JWK object to save from
* \param jwk: the JWK object to save from
* \param filename: filename to save to
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_save(struct lws_jwk *s, const char *filename);
lws_jwk_save(struct lws_jwk *jwk, const char *filename);
/** lws_jwk_rfc7638_fingerprint() - jwk to RFC7638 compliant fingerprint
*
* \param s: the JWK object to fingerprint
* \param jwk: the JWK object to fingerprint
* \param digest32: buffer to take 32-byte digest
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32);
lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32);
/** lws_jwk_strdup_meta() - allocate a duplicated string meta element
*
* \param jwk: the JWK object to fingerprint
* \param idx: JWK_META_ element index
* \param in: string to copy
* \param len: length of string to copy
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE LWS_EXTERN int
lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
const char *in, int len);
LWS_VISIBLE LWS_EXTERN int
lws_jwk_dump(struct lws_jwk *s);
lws_jwk_dump(struct lws_jwk *jwk);
/** lws_jwk_generate() - create a new key of given type and characteristics
*
* \param context: the struct lws_context used for RNG
* \param jwk: the JWK object to fingerprint
* \param kty: One of the LWS_GENCRYPTO_KTY_ key types
* \param bits: for OCT and RSA keys, the number of bits
* \param curve: for EC keys, the name of the curve
*
* Returns 0 for OK or -1 for failure
*/
LWS_VISIBLE int
lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
enum lws_gencrypto_kty kty, int bits, const char *curve);
///@}

View file

@ -29,26 +29,144 @@
* SHA256/384/512 HMAC, and RSA 256/384/512 are supported.
*
* The API uses your TLS library crypto, but works exactly the same no matter
* what you TLS backend is.
* what your TLS backend is.
*/
///@{
/*
* The maps are built to work with both JWS (LJWS_) and JWE (LJWE_), and are
* sized to the slightly larger JWE case.
*/
enum enum_jws_sig_elements {
/* JWS block namespace */
LJWS_JOSE,
LJWS_PYLD,
LJWS_SIG,
LJWS_UHDR,
/* JWE block namespace */
LJWE_JOSE = 0,
LJWE_EKEY,
LJWE_IV,
LJWE_CTXT,
LJWE_ATAG,
LWS_JWS_MAX_COMPACT_BLOCKS
};
struct lws_jws_compact_map {
const char *buf[LWS_JWS_MAX_COMPACT_BLOCKS];
uint16_t len[LWS_JWS_MAX_COMPACT_BLOCKS];
};
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) */
struct lws_jws_compact_map map, map_b64;
};
/* jws EC signatures do not have ASN.1 in them, meaning they're incompatible
* with generic signatures.
*/
/**
* lws_jws_init() - initialize a jws for use
*
* \param jws: pointer to the jws to initialize
*/
LWS_VISIBLE LWS_EXTERN void
lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk,
struct lws_context *context);
/**
* lws_jws_destroy() - scrub a jws
*
* \param jws: pointer to the jws to destroy
*
* Call before the jws goes out of scope.
*
* Elements defined in the jws are zeroed.
*/
LWS_VISIBLE LWS_EXTERN void
lws_jws_destroy(struct lws_jws *jws);
/**
* lws_jws_sig_confirm_compact() - check signature
*
* \param map: pointers and lengths for each of the unencoded JWS elements
* \param jwk: public key
* \param content: lws_context
*
* Confirms the signature on a JWS. Use if you have non-b64 plain JWS elements
* in a map... it'll make a temp b64 version needed for comparison. See below
* for other variants.
*
* Returns 0 on match.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
struct lws_context *context);
lws_jws_sig_confirm_compact(struct lws_jws_compact_map *map, struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len);
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm_compact_b64_map(struct lws_jws_compact_map *map_b64,
struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len);
/**
* lws_jws_sig_confirm_compact_b64() - check signature on b64 compact JWS
*
* \param in: pointer to b64 jose.payload[.hdr].sig
* \param len: bytes available at \p in
* \param map: map to take decoded non-b64 content
* \param jwk: public key
* \param content: lws_context
*
* Confirms the signature on a JWS. Use if you have you have b64 compact layout
* (jose.payload.hdr.sig) as an aggregated string... it'll make a temp plain
* version needed for comparison.
*
* Returns 0 on match.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm_compact_b64(const char *in, size_t len,
struct lws_jws_compact_map *map,
struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len);
/**
* lws_jws_sig_confirm() - check signature on plain + b64 JWS elements
*
* \param map_b64: pointers and lengths for each of the b64-encoded JWS elements
* \param map: pointers and lengths for each of the unencoded JWS elements
* \param jwk: public key
* \param content: lws_context
*
* Confirms the signature on a JWS. Use if you have you already have both b64
* compact layout (jose.payload.hdr.sig) and decoded JWS elements in maps.
*
* If you had the b64 string and called lws_jws_compact_decode() on it, you
* will end up with both maps, and can use this api version, saving needlessly
* regenerating any temp map.
*
* Returns 0 on match.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_sig_confirm(struct lws_jws_compact_map *map_b64, /* b64-encoded */
struct lws_jws_compact_map *map, /* non-b64 */
struct lws_jwk *jwk, struct lws_context *context);
/**
* lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload
*
* \param b64_hdr: protected header encoded in b64, may be NULL
* \param hdr_len: bytes in b64 coding of protected header
* \param b64_pay: payload encoded in b64
* \param pay_len: bytes in b64 coding of payload
* \param b64_sig: buffer to write the b64 encoded signature into
* \param sig_len: max bytes we can write at b64_sig
* \param hash_type: one of LWS_GENHASH_TYPE_SHA[256|384|512]
* \param jwk: the struct lws_jwk containing the signing key
* \param context: the lws context (used to get random)
* \param jose: jose header information
* \param jws: information to include in the signature
* \param b64_sig: output buffer for b64 signature
* \param sig_len: size of \p b64_sig output buffer
*
* This adds a b64-coded JWS signature of the b64-encoded protected header
* and b64-encoded payload, at \p b64_sig. The signature will be as large
@ -62,11 +180,181 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
* Returns the length of the encoded signature written to \p b64_sig, or -1.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
size_t pay_len, char *b64_sig, size_t sig_len,
const struct lws_jose_jwe_alg *args,
struct lws_jwk *jwk,
struct lws_context *context);
lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws, char *b64_sig,
size_t sig_len);
/**
* lws_jws_compact_decode() - converts and maps compact serialization b64 sections
*
* \param in: the incoming compact serialized b64
* \param len: the length of the incoming compact serialized b64
* \param map: pointer to the results structure
* \param map_b64: NULL, or pointer to a second results structure taking block
* information about the undecoded b64
* \param out: buffer to hold decoded results
* \param out_len: size of out in bytes
*
* Returns number of sections (2 if "none", else 3), or -1 if illegal.
*
* map is set to point to the start and hold the length of each decoded block.
* If map_b64 is non-NULL, then it's set with information about the input b64
* blocks.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_compact_decode(const char *in, int len, struct lws_jws_compact_map *map,
struct lws_jws_compact_map *map_b64, char *out, int *out_len);
LWS_VISIBLE int
lws_jws_compact_encode(struct lws_jws_compact_map *map_b64, /* b64-encoded */
const struct lws_jws_compact_map *map, /* non-b64 */
char *buf, int *out_len);
/**
* lws_jws_write_flattened_json() - create flattened JSON sig
*
* \param jws: information to include in the signature
* \param flattened: output buffer for JSON
* \param len: size of \p flattened output buffer
*
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len);
/**
* lws_jws_write_compact() - create flattened JSON sig
*
* \param jws: information to include in the signature
* \param compact: output buffer for compact format
* \param len: size of \p flattened output buffer
*
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len);
/*
* below apis are not normally needed if dealing with whole JWS... they're
* useful for creating from scratch
*/
/**
* lws_jws_dup_element() - allocate space for an element and copy data into it
*
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param in: data to duplicate into element
* \param in_len: length of data to duplicate
* \param actual_alloc: 0 for same as in_len, else actual allocation size
*
* Copies in_len from in to temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... in_len will be copied into it.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_dup_element(struct lws_jws_compact_map *map, int idx,
char *temp, int *temp_len, const void *in, size_t in_len,
size_t actual_alloc);
/**
* lws_jws_randomize_element() - create an element and fill with random
*
* \param context: lws_context used for random
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param random_len: length of data to fill with random
* \param actual_alloc: 0 for same as random_len, else actual allocation size
*
* Randomize random_len bytes at temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... the first random_len will be filled with random.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_randomize_element(struct lws_context *context,
struct lws_jws_compact_map *map,
int idx, char *temp, int *temp_len, size_t random_len,
size_t actual_alloc);
/**
* lws_jws_alloc_element() - create an element and reserve space for content
*
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param len: logical length of element
* \param actual_alloc: 0 for same as len, else actual allocation size
*
* Allocate len bytes at temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... the element logical length will be set to len.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_alloc_element(struct lws_jws_compact_map *map, int idx, char *temp,
int *temp_len, size_t len, size_t actual_alloc);
/**
* lws_jws_encode_b64_element() - create an b64-encoded element
*
* \param map: map to create the element in
* \param idx: index of element in the map to create
* \param temp: space to allocate in
* \param temp_len: available space at temp
* \param in: pointer to unencoded input
* \param in_len: length of unencoded input
*
* Allocate len bytes at temp, if temp_len is sufficient.
*
* Returns 0 or -1 if not enough space in temp / temp_len.
*
* Over-allocation can be acheived by setting actual_alloc to the real
* allocation desired... the element logical length will be set to len.
*
* *temp_len is reduced by actual_alloc if successful.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_encode_b64_element(struct lws_jws_compact_map *map, int idx,
char *temp, int *temp_len, const void *in,
size_t in_len);
/**
* lws_jws_b64_compact_map() - find block starts and lengths in compact b64
*
* \param in: pointer to b64 jose.payload[.hdr].sig
* \param len: bytes available at \p in
* \param map: output struct with pointers and lengths for each JWS element
*
* Scans a jose.payload[.hdr].sig b64 string and notes where the blocks start
* and their length into \p map.
*
* Returns number of blocks if OK. May return <0 if malformed.
* May not fill all map entries.
*/
LWS_VISIBLE LWS_EXTERN int
lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_compact_map *map);
/**
* lws_jws_base64_enc() - encode input data into b64url data
@ -82,7 +370,8 @@ LWS_VISIBLE LWS_EXTERN int
lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
/**
* lws_jws_encode_section() - encode input data into b64url data, prepending . if not first
* lws_jws_encode_section() - encode input data into b64url data,
* prepending . if not first
*
* \param in: the incoming plaintext
* \param in_len: the length of the incoming plaintext in bytes

View file

@ -257,6 +257,10 @@ LWS_VISIBLE LWS_EXTERN void
lejp_change_callback(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason));
/* exported for use when reevaluating a path for use with a subcontext */
LWS_VISIBLE LWS_EXTERN void
lejp_check_path_match(struct lejp_ctx *ctx);
LWS_VISIBLE LWS_EXTERN int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
//@}

View file

@ -330,6 +330,22 @@ lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
LWS_VISIBLE LWS_EXTERN char *
lws_strncpy(char *dest, const char *src, size_t size);
/*
* lws_timingsafe_bcmp(): constant time memcmp
*
* \param a: first buffer
* \param b: second buffer
* \param len: count of bytes to compare
*
* Return 0 if the two buffers are the same, else nonzero.
*
* Always compares all of the buffer before returning, so it can't be used as
* a timing oracle.
*/
LWS_VISIBLE LWS_EXTERN int
lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len);
/**
* lws_get_random(): fill a buffer with platform random data
*

View file

@ -1064,7 +1064,11 @@ lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf,
/* append at the tail */
while (*head) {
if (!--sanity || head == &((*head)->next)) {
if (!--sanity) {
lwsl_err("%s: buflist reached sanity limit\n", __func__);
return -1;
}
if (*head == (*head)->next) {
lwsl_err("%s: corrupt list points to self\n", __func__);
return -1;
}
@ -1097,7 +1101,7 @@ lws_buflist_destroy_segment(struct lws_buflist **head)
struct lws_buflist *old = *head;
assert(*head);
*head = (*head)->next;
*head = old->next;
old->next = NULL;
lws_free(old);
@ -3142,6 +3146,18 @@ lws_strncpy(char *dest, const char *src, size_t size)
return dest;
}
int
lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len)
{
const uint8_t *pa = a, *pb = b;
uint8_t sum = 0;
while (len--)
sum |= (*pa++ ^ *pb++);
return sum;
}
typedef enum {
LWS_TOKZS_LEADING_WHITESPACE,

View file

@ -70,7 +70,8 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
* the buflist...
*/
lws_buflist_append_segment(&wsi->buflist_out, buf, len);
if (lws_buflist_append_segment(&wsi->buflist_out, buf, len))
return -1;
buf = NULL;
len = 0;

View file

@ -28,26 +28,33 @@ useful implementations of JWS, JWE and JWK.
### Symmetric ciphers
- All common AES varaiants: CBC, CFB128, CFB8, CTR, EVB, OFB and XTS
- All common AES varaiants: CBC, CFB128, CFB8, CTR, EVB, OFB, KW and XTS
### Asymmetric ciphers
- RSA
- EC (P-256, P-384 and P521 JWA curves)
- EC (P-256, P-384 and P-521 JWA curves)
### Payload auth and crypt
- AES_128_CBC_HMAC_SHA_256
- AES_192_CBC_HMAC_SHA_384
- AES_256_CBC_HMAC_SHA_512
- AES_128_GCM
For the required and recommended asymmetric algorithms, support currently
looks like this
|JWK kty|JWA|lws|
|---|---|---|
|EC|Recommended+|no|
|EC|Recommended+|yes|
|RSA|Required|yes|
|oct|Required|yes|
|JWE alg|JWA|lws|
|---|---|---|
|RSA1_5|Recommended-|yes (no JWE yet but lws_genrsa supports)|
|RSA1_5|Recommended-|yes|
|RSA-OAEP|Recommended+|no|
|ECDH-ES|Recommended+|no|
@ -55,7 +62,15 @@ looks like this
|---|---|---|
|HS256|Required|yes|
|RS256|Recommended+|yes|
|ES256|Recommended|no|
|ES256|Recommended|yes|
## Minimal Example tools
[JWK](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwk)
[JWS](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jws)
[JWE](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/crypto/minimal-crypto-jwe)
## API tests

192
lib/jose/jwe/jwe-aeskw.c Normal file
View file

@ -0,0 +1,192 @@
/*
* libwebsockets - JSON Web Encryption support
*
* Copyright (C) 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
* 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
*
*
* JWE code related to aeskw cbc
*
*/
#include "core/private.h"
#include "jose/jwe/private.h"
/*
* RFC3394 Key Wrap uses a 128-bit key, and bloats what it is wrapping by
* one 8-byte block. So, if you had a 32 byte plaintext CEK to wrap, after
* wrapping it becomes a 40 byte wrapped, enciphered, key.
*
* The CEK comes in from and goes out in LJWE_EKEY. So LJWE_EKEY length
* increases by 8 from calling this.
*/
int
lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len)
{
struct lws_genaes_ctx aesctx;
/* we are wrapping a key, so size for the worst case after wrap */
uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES +
LWS_JWE_RFC3394_OVERHEAD_BYTES];
int n, m, hlen = lws_genhmac_size(jose->enc_alg->hmac_type);
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_OCT) {
lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
return -1;
}
jws->map_b64.len[LJWE_JOSE] = ((jws->map.len[LJWE_JOSE] * 4) / 3) + 10;
if (*temp_len < jws->map_b64.len[LJWE_JOSE])
return -1;
jws->map_b64.buf[LJWE_JOSE] = (char *)temp;
temp += jws->map_b64.len[LJWE_JOSE];
*temp_len -= jws->map_b64.len[LJWE_JOSE];
n = lws_jws_base64_enc(jws->map.buf[LJWE_JOSE], jws->map.len[LJWE_JOSE],
(char *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_notice("%s: failed to encode JOSE hdr\n", __func__);
return -1;
}
jws->map_b64.len[LJWE_JOSE] = n;
jws->map.buf[LJWE_ATAG] = (char *)temp;
jws->map.len[LJWE_ATAG] = hlen / 2;
if (*temp_len < jws->map.len[LJWE_ATAG])
return -1;
temp += hlen / 2;
*temp_len -= hlen / 2;
jws->map.buf[LJWE_IV] = (char *)temp;
jws->map.len[LJWE_IV] = LWS_JWE_AES_IV_BYTES;
if (*temp_len < jws->map.len[LJWE_IV])
return -1;
temp += LWS_JWE_AES_IV_BYTES;
*temp_len -= LWS_JWE_AES_IV_BYTES;
/* 1) Encrypt the payload... */
/* the CEK is 256-bit in the example encrypted with a 128-bit key */
n = lws_jwe_encrypt_cbc_hs(jose, jws, (uint8_t *)jws->map.buf[LJWE_EKEY],
(uint8_t *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
return -1;
}
/* 2) Encrypt the JWE Encrypted Key: RFC3394 Key Wrap uses 64 bit blocks
* and 128-bit input key*/
if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_KW,
jws->jwk->e, 1, NULL)) {
lwsl_notice("%s: lws_genaes_create\n", __func__);
return -1;
}
/* tag size is determined by enc cipher key length */
n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_EKEY],
jws->map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL,
lws_gencrypto_bits_to_bytes(
jose->enc_alg->keybits_fixed));
m = lws_genaes_destroy(&aesctx, NULL, 0);
if (n < 0) {
lwsl_err("%s: encrypt cek fail\n", __func__);
return -1;
}
if (m < 0) {
lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
return -1;
}
jws->map.len[LJWE_EKEY] += LWS_JWE_RFC3394_OVERHEAD_BYTES;
memcpy((uint8_t *)jws->map.buf[LJWE_EKEY], enc_cek,
jws->map.len[LJWE_EKEY]);
return jws->map.len[LJWE_CTXT];
}
int
lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jose *jose,
struct lws_jws *jws)
{
struct lws_genaes_ctx aesctx;
uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES +
LWS_JWE_RFC3394_OVERHEAD_BYTES];
int n, m;
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_OCT) {
lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
return -1;
}
/* the CEK is 256-bit in the example encrypted with a 128-bit key */
if (jws->map.len[LJWE_EKEY] > sizeof(enc_cek))
return -1;
/* 1) Decrypt the JWE Encrypted Key to get the raw MAC / CEK */
if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_KW,
jws->jwk->e, 1, NULL)) {
lwsl_notice("%s: lws_genaes_create\n", __func__);
return -1;
}
/*
* Decrypt the CEK into enc_cek
* tag size is determined by enc cipher key length */
n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_EKEY],
jws->map.len[LJWE_EKEY], enc_cek, NULL, NULL, NULL,
lws_gencrypto_bits_to_bytes(
jose->enc_alg->keybits_fixed));
m = lws_genaes_destroy(&aesctx, NULL, 0);
if (n < 0) {
lwsl_err("%s: decrypt CEK fail\n", __func__);
return -1;
}
if (m < 0) {
lwsl_err("%s: lws_genaes_destroy fail\n", __func__);
return -1;
}
/* 2) Decrypt the payload */
n = lws_jwe_auth_and_decrypt_cbc_hs(jose, jws, enc_cek,
(uint8_t *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n",
__func__);
return -1;
}
return jws->map.len[LJWE_CTXT];
}

View file

@ -0,0 +1,437 @@
/*
* libwebsockets - JSON Web Encryption support
*
* Copyright (C) 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
* 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
*
*
* JWE code related to rsa + aescbc
*
*/
#include "core/private.h"
#include "jose/jwe/private.h"
int
lws_jwe_encrypt_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
uint8_t *cek, uint8_t *aad, int aad_len)
{
int n, hlen = lws_genhmac_size(jose->enc_alg->hmac_type);
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_gencrypto_keyelem el;
struct lws_genhmac_ctx hmacctx;
struct lws_genaes_ctx aesctx;
uint8_t al[8];
/* Caller must have prepared space for the results */
if (jws->map.len[LJWE_ATAG] != hlen / 2) {
lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
hlen / 2, jws->map.len[LJWE_ATAG]);
return -1;
}
if (jws->map.len[LJWE_IV] != 16) {
lwsl_notice("expected iv len %d, got %d\n", 16,
jws->map.len[LJWE_IV]);
return -1;
}
/* first create the authentication hmac */
/* JWA Section 5.2.2.1
*
* 1. The secondary keys MAC_KEY and ENC_KEY are generated from the
* input key K as follows. Each of these two keys is an octet
* string.
*
* MAC_KEY consists of the initial MAC_KEY_LEN octets of K, in
* order.
* ENC_KEY consists of the final ENC_KEY_LEN octets of K, in
* order.
*/
/*
* 2. The IV used is a 128-bit value generated randomly or
* pseudorandomly for use in the cipher.
*/
lws_get_random(jws->context, (void *)jws->map.buf[LJWE_IV], 16);
/*
* 3. The plaintext is CBC encrypted using PKCS #7 padding using
* ENC_KEY as the key and the IV. We denote the ciphertext output
* from this step as E.
*/
/* second half is the AES ENC_KEY */
el.buf = (uint8_t *)jws->map.buf[LJWE_EKEY] + (hlen / 2);
el.len = hlen / 2;
if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_CBC, &el,
LWS_GAESP_NO_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
/*
* the plaintext gets delivered to us in LJWE_CTXT, this replaces
* the plaintext there with the same amount of ciphertext
*/
n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
jws->map.len[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_IV], NULL, NULL, 16);
lws_genaes_destroy(&aesctx, NULL, 0);
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
/*
* 4. The octet string AL is equal to the number of bits in the
* Additional Authenticated Data A expressed as a 64-bit unsigned
* big-endian integer.
*/
lws_jwe_be64(aad_len * 8, al);
/* first half of the CEK is the MAC key */
if (lws_genhmac_init(&hmacctx, jose->enc_alg->hmac_type,
(uint8_t *)jws->map.buf[LJWE_EKEY], hlen / 2))
return -1;
/*
* 5. A message Authentication Tag T is computed by applying HMAC
* [RFC2104] to the following data, in order:
*
* - the Additional Authenticated Data A,
* - the Initialization Vector IV,
* - the ciphertext E computed in the previous step, and
* - the octet string AL defined above.
*
* The string MAC_KEY is used as the MAC key. We denote the output
* of the MAC computed in this step as M. The first T_LEN octets of
* M are used as T.
*/
if (lws_genhmac_update(&hmacctx, aad, aad_len) ||
lws_genhmac_update(&hmacctx, jws->map.buf[LJWE_IV],
LWS_JWE_AES_IV_BYTES) ||
/* since we encrypted it, this is the ciphertext */
lws_genhmac_update(&hmacctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
jws->map.len[LJWE_CTXT]) ||
lws_genhmac_update(&hmacctx, al, 8)) {
lwsl_err("%s: hmac computation failed\n", __func__);
lws_genhmac_destroy(&hmacctx, NULL);
return -1;
}
if (lws_genhmac_destroy(&hmacctx, digest)) {
lwsl_err("%s: problem destroying hmac\n", __func__);
return -1;
}
/* create tag */
memcpy((void *)jws->map.buf[LJWE_ATAG], digest, hlen / 2);
return jws->map.len[LJWE_CTXT];
}
/*
* Requirements on entry:
*
* - jws->map LJWE_JOSE contains the ASCII JOSE header
* - jws->map LJWE_EKEY contains cek of enc_alg hmac length
* - jws->map LJWE_CTXT contains the plaintext
*
* On successful exit:
*
* - jws->map LJWE_ATAG contains the tag
* - jws->map LJWE_IV contains the new random IV that was used
* - jws->map LJWE_EKEY contains the encrypted CEK
* - jws->map LJWE_CTXT contains the ciphertext
*
* Return the amount of temp used, or -1
*/
int
lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len)
{
int n, hlen = lws_genhmac_size(jose->enc_alg->hmac_type), want;
char ekey[LWS_GENHASH_LARGEST];
struct lws_genrsa_ctx rsactx;
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
return -1;
}
/*
* Reserve space in caller temp for extra JWE elements and b64 version
* of the JOSE hdr needed for computation... notice that the
* unencrypted EKEY coming in is smaller than the RSA-encrypted EKEY
* going out, which is going to be the RSA key size
*/
want = lws_base64_size(jws->map.len[LJWE_JOSE]) +
jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len +
(hlen / 2) + LWS_JWE_AES_IV_BYTES;
if (*temp_len < want) {
lwsl_notice("%s: more temp space needed: want %d, got %d\n",
__func__, want, *temp_len);
return -1;
}
jws->map_b64.buf[LJWE_JOSE] = (char *)temp;
jws->map_b64.len[LJWE_JOSE] = lws_base64_size(jws->map.len[LJWE_JOSE]);
if (*temp_len < jws->map_b64.len[LJWE_JOSE])
return -1;
temp += jws->map_b64.len[LJWE_JOSE];
*temp_len -= jws->map_b64.len[LJWE_JOSE];
jws->map.buf[LJWE_ATAG] = (char *)temp;
jws->map.len[LJWE_ATAG] = hlen / 2;
if (*temp_len < jws->map.len[LJWE_ATAG])
return -1;
temp += hlen / 2;
*temp_len -= hlen / 2;
jws->map.buf[LJWE_IV] = (char *)temp;
jws->map.len[LJWE_IV] = LWS_JWE_AES_IV_BYTES;
if (*temp_len < jws->map.len[LJWE_IV])
return -1;
temp += jws->map.len[LJWE_IV];
*temp_len -= jws->map.len[LJWE_IV];
if (*temp_len < jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len)
return -1;
memcpy(temp, jws->map.buf[LJWE_EKEY], jws->map.len[LJWE_EKEY]);
jws->map.buf[LJWE_EKEY] = (char *)temp;
/*
* don't change jws->map.len[LJWE_EKEY]... it has allocation for up to
* jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len bytes now and the length
* will be set after the plaintext version is encrypted in-situ
*/
temp += jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
*temp_len -= jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
/* we need a b64u encode of the JOSE header as AAD */
n = lws_jws_base64_enc(jws->map.buf[LJWE_JOSE], jws->map.len[LJWE_JOSE],
(char *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_notice("%s: failed to encode JOSE hdr\n", __func__);
return -1;
}
jws->map_b64.len[LJWE_JOSE] = n;
/* Encrypt using the raw CEK (treated as MAC KEY | ENC KEY) */
n = lws_jwe_encrypt_cbc_hs(jose, jws,
(uint8_t *)jws->map.buf[LJWE_EKEY],
(uint8_t *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_encrypt_cbc_hs failed\n", __func__);
return -1;
}
if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
!strcmp(jose->alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
/* encrypt the CEK using RSA, mbedtls can't handle both in and out are
* the EKEY, so copy the unencrypted ekey out temporarily */
memcpy(ekey, jws->map.buf[LJWE_EKEY], hlen);
n = lws_genrsa_public_encrypt(&rsactx, (uint8_t *)ekey, hlen,
(uint8_t *)jws->map.buf[LJWE_EKEY]);
lws_genrsa_destroy(&rsactx);
lws_explicit_bzero(ekey, hlen);
if (n < 0) {
lwsl_err("%s: decrypt cek fail\n", __func__);
return -1;
}
jws->map.len[LJWE_EKEY] = n; /* update to encrypted EKEY size */
/*
* We end up with IV, ATAG, set, EKEY encrypted and CTXT is ciphertext,
* and b64u version of ATAG in map_b64.
*/
return 0;
}
int
lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jose *jose,
struct lws_jws *jws, uint8_t *enc_cek,
uint8_t *aad, int aad_len)
{
int n, hlen = lws_genhmac_size(jose->enc_alg->hmac_type);
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_gencrypto_keyelem el;
struct lws_genhmac_ctx hmacctx;
struct lws_genaes_ctx aesctx;
uint8_t al[8];
/* Some sanity checks on what came in */
if (jws->map.len[LJWE_ATAG] != hlen / 2) {
lwsl_notice("%s: expected tag len %d, got %d\n", __func__,
hlen / 2, jws->map.len[LJWE_ATAG]);
return -1;
}
if (jws->map.len[LJWE_IV] != 16) {
lwsl_notice("expected iv len %d, got %d\n", 16,
jws->map.len[LJWE_IV]);
return -1;
}
/* Prepare to check authentication
*
* AAD is the b64 JOSE header.
*
* The octet string AL, which is the number of bits in AAD expressed as
* a big-endian 64-bit unsigned integer is:
*
* [0, 0, 0, 0, 0, 0, 1, 152]
*
* Concatenate the AAD, the Initialization Vector, the ciphertext, and
* the AL value.
*
*/
lws_jwe_be64(aad_len * 8, al);
/* first half of enc_cek is the MAC key */
if (lws_genhmac_init(&hmacctx, jose->enc_alg->hmac_type, enc_cek,
hlen / 2))
return -1;
if (lws_genhmac_update(&hmacctx, aad, aad_len) ||
lws_genhmac_update(&hmacctx, (uint8_t *)jws->map.buf[LJWE_IV],
jws->map.len[LJWE_IV]) ||
lws_genhmac_update(&hmacctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
jws->map.len[LJWE_CTXT]) ||
lws_genhmac_update(&hmacctx, al, 8)) {
lwsl_err("%s: hmac computation failed\n", __func__);
lws_genhmac_destroy(&hmacctx, NULL);
return -1;
}
if (lws_genhmac_destroy(&hmacctx, digest)) {
lwsl_err("%s: problem destroying hmac\n", __func__);
return -1;
}
/* first half of digest is the auth tag */
if (lws_timingsafe_bcmp(digest, jws->map.buf[LJWE_ATAG], hlen / 2)) {
lwsl_err("%s: auth failed: hmac tag != ATAG\n", __func__);
lwsl_hexdump_notice(jws->map.buf[LJWE_ATAG], hlen / 2);
lwsl_hexdump_notice(digest, 16);
return -1;
}
/* second half of enc cek is the CEK KEY */
el.buf = enc_cek + (hlen / 2);
el.len = hlen / 2;
if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_CBC,
&el, LWS_GAESP_NO_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
jws->map.len[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_IV], NULL, NULL, 16);
n |= lws_genaes_destroy(&aesctx, NULL, 0);
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
return jws->map.len[LJWE_CTXT];
}
int
lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jose *jose,
struct lws_jws *jws)
{
int n;
struct lws_genrsa_ctx rsactx;
uint8_t enc_cek[512];
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
return -1;
}
if (jws->map.len[LJWE_EKEY] < 40) {
lwsl_err("%s: EKEY length too short %d\n", __func__,
jws->map.len[LJWE_EKEY]);
return -1;
}
/* Decrypt the JWE Encrypted Key to get the raw MAC || CEK */
if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
!strcmp(jose->alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = lws_genrsa_private_decrypt(&rsactx,
(uint8_t *)jws->map.buf[LJWE_EKEY],
jws->map.len[LJWE_EKEY], enc_cek,
sizeof(enc_cek));
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_err("%s: decrypt cek fail: \n", __func__);
return -1;
}
n = lws_jwe_auth_and_decrypt_cbc_hs(jose, jws, enc_cek,
(uint8_t *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt_cbc_hs failed\n",
__func__);
return -1;
}
return jws->map.len[LJWE_CTXT];
}

View file

@ -0,0 +1,349 @@
/*
* libwebsockets - JSON Web Encryption support
*
* Copyright (C) 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
* 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
*
*
* JWE code related to aes gcm
*
*/
#include "core/private.h"
#include "jose/jwe/private.h"
#define LWS_AESGCM_IV 12
#define LWS_AESGCM_TAG 16
/*
* NOTICE this is AESGCM content encryption, it's not AES GCM key wrapping
*
*
* This section defines the specifics of performing authenticated
* encryption with AES in Galois/Counter Mode (GCM) ([AES] and
* [NIST.800-38D]).
*
* The CEK is used as the encryption key.
*
* Use of an IV of size 96 bits is REQUIRED with this algorithm.
*
* The requested size of the Authentication Tag output MUST be 128 bits,
* regardless of the key size.
*
* For decrypt: decrypt the KEK, then decrypt the payload
*
* For encrypt: encrypt the payload, then encrypt the KEK
*/
/*
* encrypting... enc_cek is unencrypted
*/
int
lws_jwe_encrypt_gcm(struct lws_jose *jose, struct lws_jws *jws,
uint8_t *enc_cek, uint8_t *aad, int aad_len)
{
struct lws_gencrypto_keyelem el;
struct lws_genaes_ctx aesctx;
size_t ivs = LWS_AESGCM_IV;
int n;
/* Some sanity checks on what came in */
/* MUST be 128-bit for all sizes */
if (jws->map.len[LJWE_ATAG] != LWS_AESGCM_TAG) {
lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n",
__func__, jws->map.len[LJWE_ATAG]);
return -1;
}
if (jws->map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */
lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__,
jws->map.len[LJWE_IV]);
return -1;
}
/* EKEY is directly the CEK KEY */
el.buf = enc_cek;
el.len = jose->enc_alg->keybits_fixed / 8;
if (lws_genaes_create(&aesctx, LWS_GAESO_ENC, LWS_GAESM_GCM,
&el, LWS_GAESP_NO_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
/* aad */
n = lws_genaes_crypt(&aesctx, aad, aad_len, NULL,
(uint8_t *)jws->map.buf[LJWE_IV],
(uint8_t *)jws->map.buf[LJWE_ATAG], &ivs,
LWS_AESGCM_TAG);
if (n) {
lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__);
return -1;
}
/* payload */
n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
jws->map.len[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_IV],
NULL, &ivs,
LWS_AESGCM_TAG);
n |= lws_genaes_destroy(&aesctx, (uint8_t *)jws->map.buf[LJWE_ATAG],
LWS_AESGCM_TAG);
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
return jws->map.len[LJWE_CTXT];
}
int
lws_jwe_encrypt_rsa_aes_gcm(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len)
{
int n, ret = -1, used = 0;
struct lws_genrsa_ctx rsactx;
uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
int ekbytes = jose->enc_alg->keybits_fixed / 8;
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
return -1;
}
if (jws->map.len[LJWE_EKEY] < 32) {
lwsl_err("%s: EKEY length too short %d\n", __func__,
jws->map.len[LJWE_EKEY]);
return -1;
}
/* create the IV + CEK */
jws->map_b64.len[LJWE_JOSE] = lws_base64_size(jws->map.len[LJWE_JOSE]);
if (*temp_len < LWS_AESGCM_IV + LWS_AESGCM_TAG + ekbytes +
jws->map_b64.len[LJWE_JOSE])
return -1;
*temp_len -= LWS_AESGCM_IV + LWS_AESGCM_TAG +
jws->map_b64.len[LJWE_JOSE] +
(jose->enc_alg->keybits_fixed / 8);
if (lws_get_random(jws->context, temp, LWS_AESGCM_IV) != LWS_AESGCM_IV)
return -1;
jws->map.buf[LJWE_IV] = temp;
jws->map.len[LJWE_IV] = LWS_AESGCM_IV;
temp += LWS_AESGCM_IV;
jws->map.buf[LJWE_ATAG] = temp;
jws->map.len[LJWE_ATAG] = LWS_AESGCM_TAG;
temp += LWS_AESGCM_TAG;
/* we create the CEK into EKEY, it'll be cleansed by jws destroy */
if (lws_get_random(jws->context, temp, ekbytes) != ekbytes)
return -1;
jws->map.buf[LJWE_EKEY] = temp;
jws->map.len[LJWE_EKEY] = ekbytes;
temp += ekbytes;
jws->map_b64.buf[LJWE_JOSE] = temp;
temp += jws->map_b64.len[LJWE_JOSE];
/* we need a b64u encode of the JOSE header as AAD */
n = lws_jws_base64_enc(jws->map.buf[LJWE_JOSE], jws->map.len[LJWE_JOSE],
(char *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_notice("%s: failed to encode JOSE hdr\n", __func__);
return -1;
}
jws->map_b64.len[LJWE_JOSE] = n;
/* we must cleanse enc_cek */
used = jws->map.len[LJWE_EKEY];
memcpy(enc_cek, jws->map.buf[LJWE_EKEY], jws->map.len[LJWE_EKEY]);
/* encrypt the payload */
n = lws_jwe_encrypt_gcm(jose, jws, (uint8_t *)jws->map.buf[LJWE_EKEY],
(uint8_t *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_encrypt_gcm failed\n",
__func__);
goto bail;
}
/* Encrypt the CEK to make the JWE Encrypted Key */
if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
!strcmp(jose->alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_SHA1 /* !!! */)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
goto bail;
}
n = lws_genrsa_public_encrypt(&rsactx,
(uint8_t *)jws->map.buf[LJWE_EKEY],
jws->map.len[LJWE_EKEY], enc_cek);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_err("%s: encrypt cek fail: \n", __func__);
goto bail;
}
jws->map.len[LJWE_EKEY] = n;
/* overwrite the CEK in EKEY with the encrypted version */
memcpy((void *)jws->map.buf[LJWE_EKEY], enc_cek,
jws->map.len[LJWE_EKEY]);
ret = jws->map.len[LJWE_CTXT];
bail:
/* cleanse enc_cek on stack that contained the unencrypted CEK */
lws_explicit_bzero(enc_cek, used);
return ret;
}
int
lws_jwe_auth_and_decrypt_gcm(struct lws_jose *jose, struct lws_jws *jws,
uint8_t *enc_cek, uint8_t *aad, int aad_len)
{
struct lws_gencrypto_keyelem el;
struct lws_genaes_ctx aesctx;
size_t ivs = LWS_AESGCM_IV;
uint8_t tag[LWS_AESGCM_TAG];
int n;
/* Some sanity checks on what came in */
/* Tag MUST be 128-bit for all sizes */
if (jws->map.len[LJWE_ATAG] != LWS_AESGCM_TAG) {
lwsl_notice("%s: AESGCM tag size must be 128b, got %d\n",
__func__, jws->map.len[LJWE_ATAG]);
return -1;
}
if (jws->map.len[LJWE_IV] != LWS_AESGCM_IV) { /* MUST be 96-bit */
lwsl_notice("%s: AESGCM IV must be 128b, got %d\n", __func__,
jws->map.len[LJWE_IV]);
return -1;
}
/* EKEY is directly the CEK KEY */
el.buf = enc_cek;
el.len = jose->enc_alg->keybits_fixed / 8;
if (lws_genaes_create(&aesctx, LWS_GAESO_DEC, LWS_GAESM_GCM,
&el, LWS_GAESP_NO_PADDING, NULL)) {
lwsl_err("%s: lws_genaes_create failed\n", __func__);
return -1;
}
n = lws_genaes_crypt(&aesctx, aad, aad_len,
NULL,
(uint8_t *)jws->map.buf[LJWE_IV],
(uint8_t *)jws->map.buf[LJWE_ATAG], &ivs, 16);
if (n) {
lwsl_err("%s: lws_genaes_crypt aad failed\n", __func__);
return -1;
}
n = lws_genaes_crypt(&aesctx, (uint8_t *)jws->map.buf[LJWE_CTXT],
jws->map.len[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_CTXT],
(uint8_t *)jws->map.buf[LJWE_IV],
(uint8_t *)jws->map.buf[LJWE_ATAG], &ivs, 16);
n |= lws_genaes_destroy(&aesctx, tag, sizeof(tag));
if (n) {
lwsl_err("%s: lws_genaes_crypt failed\n", __func__);
return -1;
}
return jws->map.len[LJWE_CTXT];
}
int
lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jose *jose, struct lws_jws *jws)
{
int n;
struct lws_genrsa_ctx rsactx;
uint8_t enc_cek[LWS_JWE_LIMIT_KEY_ELEMENT_BYTES];
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: unexpected kty %d\n", __func__, jws->jwk->kty);
return -1;
}
if (jws->map.len[LJWE_EKEY] < 32) {
lwsl_err("%s: EKEY length too short %d\n", __func__,
jws->map.len[LJWE_EKEY]);
return -1;
}
/* Decrypt the JWE Encrypted Key to get the direct CEK */
if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
!strcmp(jose->alg->alg, "RSA-OAEP") ?
LGRSAM_PKCS1_OAEP_PSS : LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_SHA1 /* !!! */)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = lws_genrsa_private_decrypt(&rsactx,
(uint8_t *)jws->map.buf[LJWE_EKEY],
jws->map.len[LJWE_EKEY], enc_cek,
sizeof(enc_cek));
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_err("%s: decrypt cek fail: \n", __func__);
return -1;
}
n = lws_jwe_auth_and_decrypt_gcm(jose, jws, enc_cek,
(uint8_t *)jws->map_b64.buf[LJWE_JOSE],
jws->map_b64.len[LJWE_JOSE]);
if (n < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt_gcm_hs failed\n",
__func__);
return -1;
}
return jws->map.len[LJWE_CTXT];
}

View file

@ -21,24 +21,418 @@
*
* This supports RFC7516 JSON Web Encryption
*
*
*/
#include "core/private.h"
#include "jose/jwe/private.h"
#if 0
static const char * const jwe_complete_tokens[] = {
"protected",
"recipients[].header",
"recipients[].header.alg",
"recipients[].header.kid",
"recipients[].encrypted_key",
"iv",
"ciphertext",
"tag",
};
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,
};
struct complete_cb_args {
struct lws_jws_compact_map *map;
struct lws_jws_compact_map *map_b64;
char *out;
int out_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)
{
struct complete_cb_args *args = (struct complete_cb_args *)ctx->user;
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
switch (ctx->path_match - 1) {
/* 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:
}
return 0;
}
LWS_VISIBLE int
lws_jwe_create_packet(struct lws_jwk *jwk,
const struct lws_jose_jwe_alg *jose_alg,
lws_jws_complete_decode(const char *json_in, int len,
struct lws_jws_compact_map *map,
struct lws_jws_compact_map *map_b64, char *out,
int out_len)
{
struct complete_cb_args args;
struct lejp_ctx jctx;
int blocks, n, m = 0;
if (!map_b64)
map_b64 = map;
memset(map_b64, 0, sizeof(*map_b64));
memset(map, 0, sizeof(*map));
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);
lejp_destruct(&jctx);
}
#endif
static uint8_t *
be32(uint32_t i, uint32_t *p32)
{
uint8_t *p = (uint8_t *)p32;
*p++ = (i >> 24) & 0xff;
*p++ = (i >> 16) & 0xff;
*p++ = (i >> 8) & 0xff;
*p++ = i & 0xff;
return (uint8_t *)p32;
}
/*
* The key derivation process derives the agreed-upon key from the
* shared secret Z established through the ECDH algorithm, per
* Section 6.2.2.2 of [NIST.800-56A].
*
* out must be prepared to take at least 32 bytes or the encrypted key size,
* whichever is larger.
*/
int
lws_jwa_concat_kdf(struct lws_jose *jose, struct lws_jws *jws, int direct,
uint8_t *out, const uint8_t *shared_secret, int sslen)
{
int hlen = lws_genhash_size(LWS_GENHASH_TYPE_SHA256), aidlen;
struct lws_genhash_ctx hash_ctx;
uint32_t ctr = 1, t;
const char *aid;
/*
* Hash
*
* AlgorithmID || PartyUInfo || PartyVInfo
* {|| SuppPubInfo }{|| SuppPrivInfo }
*
* AlgorithmID
*
* The AlgorithmID value is of the form Datalen || Data, where Data
* is a variable-length string of zero or more octets, and Datalen is
* a fixed-length, big-endian 32-bit counter that indicates the
* length (in octets) of Data. In the Direct Key Agreement case,
* Data is set to the octets of the ASCII representation of the "enc"
* Header Parameter value. In the Key Agreement with Key Wrapping
* case, Data is set to the octets of the ASCII representation of the
* "alg" (algorithm) Header Parameter value.
*/
aid = direct ? jose->enc_alg->alg : jose->alg->alg;
aidlen = strlen(aid);
/*
* PartyUInfo (PartyVInfo is the same deal)
*
* The PartyUInfo value is of the form Datalen || Data, where Data is
* a variable-length string of zero or more octets, and Datalen is a
* fixed-length, big-endian 32-bit counter that indicates the length
* (in octets) of Data. If an "apu" (agreement PartyUInfo) Header
* Parameter is present, Data is set to the result of base64url
* decoding the "apu" value and Datalen is set to the number of
* octets in Data. Otherwise, Datalen is set to 0 and Data is set to
* the empty octet sequence
*
* SuppPubInfo
*
* This is set to the keydatalen represented as a 32-bit big-endian
* integer.
*
* keydatalen
*
* This is set to the number of bits in the desired output key. For
* "ECDH-ES", this is length of the key used by the "enc" algorithm.
* For "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW", this
* is 128, 192, and 256, respectively.
*
* Compute Hash i = H(counter || Z || OtherInfo).
*
* We must iteratively hash over key material that's larger than
* one hash output size (256b for SHA-256)
*/
while (ctr <= (uint32_t)(jose->enc_alg->keybits_fixed / hlen)) {
/*
* Key derivation is performed using the Concat KDF, as defined
* in Section 5.8.1 of [NIST.800-56A], where the Digest Method
* is SHA-256.
*/
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
return -1;
if (/* counter */
lws_genhash_update(&hash_ctx, be32(ctr++, &t), 4) ||
/* Z */
lws_genhash_update(&hash_ctx, shared_secret, sslen) ||
/* other info */
lws_genhash_update(&hash_ctx, be32(strlen(aid), &t), 4) ||
lws_genhash_update(&hash_ctx, aid, aidlen) ||
lws_genhash_update(&hash_ctx,
be32(jose->e[LJJHI_APU].len, &t), 4) ||
lws_genhash_update(&hash_ctx, jose->e[LJJHI_APU].buf,
jose->e[LJJHI_APU].len) ||
lws_genhash_update(&hash_ctx,
be32(jose->e[LJJHI_APV].len, &t), 4) ||
lws_genhash_update(&hash_ctx, jose->e[LJJHI_APV].buf,
jose->e[LJJHI_APV].len) ||
lws_genhash_update(&hash_ctx,
be32(jose->enc_alg->keybits_fixed, &t),
4) ||
lws_genhash_destroy(&hash_ctx, out)) {
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
out += hlen;
}
return 0;
}
LWS_VISIBLE void
lws_jwe_be64(uint64_t c, uint8_t *p8)
{
int n;
for (n = 56; n >= 0; n -= 8)
*p8++ = (uint8_t)((c >> n) & 0xff);
}
LWS_VISIBLE int
lws_jwe_auth_and_decrypt(struct lws_jose *jose, struct lws_jws *jws)
{
int valid_aescbc_hmac, valid_aesgcm;
char temp[512];
int temp_len = sizeof(temp);
if (lws_jwe_parse_jose(jose, jws->map.buf[LJWS_JOSE],
jws->map.len[LJWS_JOSE],
temp, &temp_len) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
return -1;
}
valid_aescbc_hmac = jose->enc_alg &&
jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC &&
(jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 ||
jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 ||
jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512);
valid_aesgcm = jose->enc_alg &&
jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM;
/* RSA + AESCBC */
if ((!strcmp(jose->alg->alg, "RSA1_5") ||
!strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aescbc_hmac)
return lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(jose, jws);
/* RSA + AESGCM */
if ((!strcmp(jose->alg->alg, "RSA1_5") ||
!strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aesgcm)
return lws_jwe_auth_and_decrypt_rsa_aes_gcm(jose, jws);
/* AESKW */
if ((!strcmp(jose->alg->alg, "A128KW") ||
!strcmp(jose->alg->alg, "A192KW") ||
!strcmp(jose->alg->alg, "A256KW")) && valid_aescbc_hmac)
return lws_jwe_auth_and_decrypt_aeskw_cbc_hs(jose, jws);
lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__,
jose->alg->alg, jose->enc_alg ?
jose->enc_alg->alg : "NULL");
return -1;
}
LWS_VISIBLE int
lws_jwe_encrypt(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len)
{
int valid_aescbc_hmac, valid_aesgcm;
valid_aesgcm = jose->enc_alg &&
jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_GCM;
if (lws_jwe_parse_jose(jose, jws->map.buf[LJWS_JOSE],
jws->map.len[LJWS_JOSE], temp, temp_len) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
return -1;
}
valid_aescbc_hmac = jose->enc_alg &&
jose->enc_alg->algtype_crypto == LWS_JOSE_ENCTYPE_AES_CBC &&
(jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA256 ||
jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA384 ||
jose->enc_alg->hmac_type == LWS_GENHMAC_TYPE_SHA512);
/* RSA + AESCBC */
if ((!strcmp(jose->alg->alg, "RSA1_5") ||
!strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aescbc_hmac)
return lws_jwe_encrypt_rsa_aes_cbc_hs(jose, jws, temp, temp_len);
/* RSA + AESGCM */
if ((!strcmp(jose->alg->alg, "RSA1_5") ||
!strcmp(jose->alg->alg, "RSA-OAEP")) && valid_aesgcm)
return lws_jwe_encrypt_rsa_aes_gcm(jose, jws, temp, temp_len);
/* AESKW */
if ((!strcmp(jose->alg->alg, "A128KW") ||
!strcmp(jose->alg->alg, "A192KW") ||
!strcmp(jose->alg->alg, "A256KW")) && valid_aescbc_hmac)
return lws_jwe_encrypt_aeskw_cbc_hs(jose, jws, temp, temp_len);
lwsl_err("%s: unknown cipher alg combo %s / %s\n", __func__,
jose->alg->alg, jose->enc_alg ?
jose->enc_alg->alg : "NULL");
return -1;
}
/*
* JWE Compact Serialization consists of
*
* BASE64URL(UTF8(JWE Protected Header)) || '.' ||
* BASE64URL(JWE Encrypted Key) || '.' ||
* BASE64URL(JWE Initialization Vector) || '.' ||
* BASE64URL(JWE Ciphertext) || '.' ||
* BASE64URL(JWE Authentication Tag)
*/
LWS_VISIBLE int
lws_jwe_write_compact(struct lws_jose *jose, struct lws_jws *jws,
char *out, size_t out_len)
{
size_t orig = out_len;
int n;
n = lws_jws_base64_enc(jws->map.buf[LJWS_JOSE],
jws->map.len[LJWS_JOSE], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode JOSE\n", __func__);
return n;
}
out += n;
*out++ = '.';
out_len -= n + 1;
n = lws_jws_base64_enc(jws->map.buf[LJWE_EKEY],
jws->map.len[LJWE_EKEY], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode EKEY\n", __func__);
return n;
}
out += n;
*out++ = '.';
out_len -= n + 1;
n = lws_jws_base64_enc(jws->map.buf[LJWE_IV],
jws->map.len[LJWE_IV], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode IV\n", __func__);
return n;
}
out += n;
*out++ = '.';
out_len -= n + 1;
n = lws_jws_base64_enc(jws->map.buf[LJWE_CTXT],
jws->map.len[LJWE_CTXT], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode CTXT\n", __func__);
return n;
}
out += n;
*out++ = '.';
out_len -= n + 1;
n = lws_jws_base64_enc(jws->map.buf[LJWE_ATAG],
jws->map.len[LJWE_ATAG], out, out_len);
if (n < 0 || (int)out_len == n) {
lwsl_info("%s: unable to encode ATAG\n", __func__);
return n;
}
out += n;
*out++ = '\0';
out_len -= n;
return orig - out_len;
}
LWS_VISIBLE int
lws_jwe_create_packet(struct lws_jose *jose, struct lws_jwk *jwk,
const char *payload, size_t len,
const char *nonce, char *out, size_t out_len,
struct lws_context *context)
{
char *buf, *start, *p, *end, *p1, *end1, *b64_hdr, *b64_pay;
int n, b64_hdr_len, b64_pay_len;
char *buf, *start, *p, *end, *p1, *end1;
struct lws_jws jws;
int n;
lws_jws_init(&jws, jwk, context);
/*
* This buffer is local to the function, the actual output
* is prepared into vhd->buf. Only the plaintext protected header
* This buffer is local to the function, the actual output is prepared
* into vhd->buf. Only the plaintext protected header
* (which contains the public key, 512 bytes for 4096b) goes in
* here temporarily.
*/
@ -56,7 +450,11 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
* temporary JWS protected header plaintext
*/
p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
if (!jose->alg || !jose->alg->alg)
goto bail;
p += lws_snprintf(p, end - p, "{\"alg\":\"%s\",\"jwk\":",
jose->alg->alg);
n = lws_jwk_export(jwk, 0, p, end - p);
if (n < 0) {
lwsl_notice("failed to export jwk\n");
@ -74,23 +472,33 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
end1 = out + out_len - 1;
p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\"");
b64_hdr = p1;
jws.map_b64.buf[LJWS_JOSE] = p1;
n = lws_jws_base64_enc(start, p - start, p1, end1 - p1);
if (n < 0) {
lwsl_notice("%s: failed to encode protected\n", __func__);
goto bail;
}
b64_hdr_len = n;
jws.map_b64.len[LJWS_JOSE] = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
b64_pay = p1;
jws.map_b64.buf[LJWS_PYLD] = p1;
n = lws_jws_base64_enc(payload, len, p1, end1 - p1);
if (n < 0) {
lwsl_notice("%s: failed to encode payload\n", __func__);
goto bail;
}
b64_pay_len = n;
jws.map_b64.len[LJWS_PYLD] = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\",\"header\":\"");
jws.map_b64.buf[LJWS_UHDR] = p1;
n = lws_jws_base64_enc(payload, len, p1, end1 - p1);
if (n < 0) {
lwsl_notice("%s: failed to encode payload\n", __func__);
goto bail;
}
jws.map_b64.len[LJWS_UHDR] = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\"");
@ -99,13 +507,15 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
* taking the b64 protected header and the b64 payload, sign them
* and place the signature into the packet
*/
n = lws_jws_sign_from_b64(b64_hdr, b64_hdr_len, b64_pay, b64_pay_len,
p1, end1 - p1, jose_alg, jwk, context);
n = lws_jws_sign_from_b64(jose, &jws, p1, end1 - p1);
if (n < 0) {
lwsl_notice("sig gen failed\n");
goto bail;
}
jws.map_b64.buf[LJWS_SIG] = p1;
jws.map_b64.len[LJWS_SIG] = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\"}");
@ -114,6 +524,7 @@ lws_jwe_create_packet(struct lws_jwk *jwk,
return p1 - out;
bail:
lws_jws_destroy(&jws);
free(buf);
return -1;

50
lib/jose/jwe/private.h Normal file
View file

@ -0,0 +1,50 @@
/* jwe-rsa-aescbc.c */
int
lws_jwe_encrypt_rsa_aes_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len);
int
lws_jwe_auth_and_decrypt_cbc_hs(struct lws_jose *jose,
struct lws_jws *jws, uint8_t *enc_cek,
uint8_t *aad, int aad_len);
int
lws_jwe_auth_and_decrypt_rsa_aes_cbc_hs(struct lws_jose *jose,
struct lws_jws *jws);
int
lws_jwe_encrypt_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
uint8_t *cek, uint8_t *aad, int aad_len);
/* jws-rsa-aesgcm.c */
int
lws_jwe_auth_and_decrypt_gcm(struct lws_jose *jose,
struct lws_jws *jws, uint8_t *enc_cek,
uint8_t *aad, int aad_len);
int
lws_jwe_auth_and_decrypt_rsa_aes_gcm(struct lws_jose *jose,
struct lws_jws *jws);
int
lws_jwe_encrypt_gcm(struct lws_jose *jose, struct lws_jws *jws,
uint8_t *enc_cek, uint8_t *aad, int aad_len);
int
lws_jwe_encrypt_rsa_aes_gcm(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len);
/* jwe-rsa-aeskw.c */
int
lws_jwe_encrypt_aeskw_cbc_hs(struct lws_jose *jose, struct lws_jws *jws,
char *temp, int *temp_len);
int
lws_jwe_auth_and_decrypt_aeskw_cbc_hs(struct lws_jose *jose,
struct lws_jws *jws);

View file

@ -20,15 +20,16 @@
*/
#include "core/private.h"
#include "jose/private.h"
#include <fcntl.h>
#include <unistd.h>
static const char * const kyt_names[] = {
"unknown", /* LWS_GENCRYPTO_KYT_UNKNOWN */
"oct", /* LWS_GENCRYPTO_KYT_OCT */
"RSA", /* LWS_GENCRYPTO_KYT_RSA */
"EC" /* LWS_GENCRYPTO_KYT_EC */
static const char * const kty_names[] = {
"unknown", /* LWS_GENCRYPTO_KTY_UNKNOWN */
"oct", /* LWS_GENCRYPTO_KTY_OCT */
"RSA", /* LWS_GENCRYPTO_KTY_RSA */
"EC" /* LWS_GENCRYPTO_KTY_EC */
};
/*
@ -64,42 +65,84 @@ static const char * const jwk_tok[] = {
/* information about each token declared above */
#define F_B64 (1 << 10)
#define F_B64U (1 << 11)
#define F_META (1 << 12)
#define F_RSA (1 << 13)
#define F_EC (1 << 14)
#define F_OCT (1 << 15)
#define F_M (1 << 9) /* Mandatory for key type */
#define F_B64 (1 << 10) /* Base64 coded octets */
#define F_B64U (1 << 11) /* Base64 Url coded octets */
#define F_META (1 << 12) /* JWK key metainformation */
#define F_RSA (1 << 13) /* RSA key */
#define F_EC (1 << 14) /* Elliptic curve key */
#define F_OCT (1 << 15) /* octet key */
unsigned short tok_map[] = {
static unsigned short tok_map[] = {
F_RSA | F_EC | F_OCT | F_META | 0xff,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_E,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_N,
F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI,
F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_E,
F_RSA | F_B64U | F_M | LWS_GENCRYPTO_RSA_KEYEL_N,
F_RSA | F_EC | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_D,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_P,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_Q,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DP,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_DQ,
F_RSA | F_B64U | LWS_GENCRYPTO_RSA_KEYEL_QI,
F_RSA | F_EC | F_OCT | F_META | JWK_META_KTY,
F_OCT | F_B64U | LWS_GENCRYPTO_OCT_KEYEL_K,
F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY,
F_OCT | F_B64U | F_M | LWS_GENCRYPTO_OCT_KEYEL_K,
F_EC | LWS_GENCRYPTO_EC_KEYEL_CRV,
F_EC | F_B64U | LWS_GENCRYPTO_EC_KEYEL_X,
F_EC | F_B64U | LWS_GENCRYPTO_EC_KEYEL_Y,
F_EC | F_M | LWS_GENCRYPTO_EC_KEYEL_CRV,
F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_X,
F_EC | F_B64U | F_M | LWS_GENCRYPTO_EC_KEYEL_Y,
F_RSA | F_EC | F_OCT | F_META | JWK_META_KID,
F_RSA | F_EC | F_OCT | F_META | JWK_META_USE,
F_RSA | F_EC | F_OCT | F_META | JWK_META_KID,
F_RSA | F_EC | F_OCT | F_META | JWK_META_USE,
F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS,
F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C,
F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG,
F_RSA | F_EC | F_OCT | F_META | JWK_META_KEY_OPS,
F_RSA | F_EC | F_OCT | F_META | F_B64 | JWK_META_X5C,
F_RSA | F_EC | F_OCT | F_META | JWK_META_ALG,
};
static const char *meta_names[] = {
"kty", "kid", "use", "key_ops", "x5c", "alg"
};
struct lexico {
const char *name;
int idx;
char meta;
} lexico_ec[] = {
{ "alg", JWK_META_ALG, 1 },
{ "crv", LWS_GENCRYPTO_EC_KEYEL_CRV, 0 },
{ "d", LWS_GENCRYPTO_EC_KEYEL_D, 2 | 0 },
{ "key_ops", JWK_META_KEY_OPS, 1 },
{ "kid", JWK_META_KID, 1 },
{ "kty", JWK_META_KTY, 1 },
{ "use", JWK_META_USE, 1 },
{ "x", LWS_GENCRYPTO_EC_KEYEL_X, 0 },
{ "x5c", JWK_META_X5C, 1 },
{ "y", LWS_GENCRYPTO_EC_KEYEL_Y, 0 }
}, lexico_oct[] = {
{ "alg", JWK_META_ALG, 1 },
{ "k", LWS_GENCRYPTO_OCT_KEYEL_K, 0 },
{ "key_ops", JWK_META_KEY_OPS, 1 },
{ "kid", JWK_META_KID, 1 },
{ "kty", JWK_META_KTY, 1 },
{ "use", JWK_META_USE, 1 },
{ "x5c", JWK_META_X5C, 1 }
}, lexico_rsa[] = {
{ "alg", JWK_META_ALG, 1 },
{ "d", LWS_GENCRYPTO_RSA_KEYEL_D, 2 | 0 },
{ "dp", LWS_GENCRYPTO_RSA_KEYEL_DP, 2 | 0 },
{ "dq", LWS_GENCRYPTO_RSA_KEYEL_DQ, 2 | 0 },
{ "e", LWS_GENCRYPTO_RSA_KEYEL_E, 0 },
{ "key_ops", JWK_META_KEY_OPS, 1 },
{ "kid", JWK_META_KID, 1 },
{ "kty", JWK_META_KTY, 1 },
{ "n", LWS_GENCRYPTO_RSA_KEYEL_N, 0 },
{ "p", LWS_GENCRYPTO_RSA_KEYEL_P, 2 | 0 },
{ "q", LWS_GENCRYPTO_RSA_KEYEL_Q, 2 | 0 },
{ "qi", LWS_GENCRYPTO_RSA_KEYEL_QI, 2 | 0 },
{ "use", JWK_META_USE, 1 },
{ "x5c", JWK_META_X5C, 1 }
};
static const char meta_b64[] = { 0, 0, 0, 0, 1, 0 };
static const char *oct_names[] = {
@ -117,72 +160,65 @@ static const char *ec_names[] = {
};
static const char ec_b64[] = { 0, 1, 1, 1 };
struct cb_lws_jwk {
struct lws_jwk *s;
char *b64;
lws_jwk_key_import_callback per_key_cb;
void *user;
int b64max;
int pos;
unsigned short possible;
};
LWS_VISIBLE int
lws_jwk_dump(struct lws_jwk *s)
lws_jwk_dump(struct lws_jwk *jwk)
{
const char **enames, *b64;
int elems;
int n;
switch (s->kty) {
(void)enames;
(void)meta_names;
switch (jwk->kty) {
default:
case LWS_GENCRYPTO_KYT_UNKNOWN:
lwsl_err("%s: jwk %p: unknown type\n", __func__, s);
case LWS_GENCRYPTO_KTY_UNKNOWN:
lwsl_err("%s: jwk %p: unknown type\n", __func__, jwk);
return 1;
case LWS_GENCRYPTO_KYT_OCT:
case LWS_GENCRYPTO_KTY_OCT:
elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT;
enames = oct_names;
b64 = oct_b64;
break;
case LWS_GENCRYPTO_KYT_RSA:
case LWS_GENCRYPTO_KTY_RSA:
elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT;
enames = rsa_names;
b64 = rsa_b64;
break;
case LWS_GENCRYPTO_KYT_EC:
case LWS_GENCRYPTO_KTY_EC:
elems = LWS_GENCRYPTO_EC_KEYEL_COUNT;
enames = ec_names;
b64 = ec_b64;
break;
}
lwsl_info("%s: jwk %p\n", __func__, s);
lwsl_info("%s: jwk %p\n", __func__, jwk);
for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++) {
if (s->meta[n].buf && meta_b64[n]) {
if (jwk->meta[n].buf && meta_b64[n]) {
lwsl_info(" meta: %s\n", meta_names[n]);
lwsl_hexdump_info(s->meta[n].buf, s->meta[n].len);
lwsl_hexdump_info(jwk->meta[n].buf, jwk->meta[n].len);
}
if (s->meta[n].buf && !meta_b64[n])
if (jwk->meta[n].buf && !meta_b64[n])
lwsl_info(" meta: %s: '%s'\n", meta_names[n],
s->meta[n].buf);
jwk->meta[n].buf);
}
for (n = 0; n < elems; n++) {
if (s->e[n].buf && b64[n]) {
if (jwk->e[n].buf && b64[n]) {
lwsl_info(" e: %s\n", enames[n]);
lwsl_hexdump_info(s->e[n].buf, s->e[n].len);
lwsl_hexdump_info(jwk->e[n].buf, jwk->e[n].len);
}
if (s->e[n].buf && !b64[n])
lwsl_info(" e: %s: '%s'\n", enames[n], s->e[n].buf);
if (jwk->e[n].buf && !b64[n])
lwsl_info(" e: %s: '%s'\n", enames[n], jwk->e[n].buf);
}
return 0;
}
static int
_lws_jwk_set_element_jwk(struct lws_gencrypto_keyelem *e, char *in, int len)
_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, int len)
{
e->buf = lws_malloc(len + 1, "jwk");
if (!e->buf)
@ -196,9 +232,9 @@ _lws_jwk_set_element_jwk(struct lws_gencrypto_keyelem *e, char *in, int len)
}
static int
_lws_jwk_set_element_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len)
_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len)
{
int dec_size = ((len * 3) / 4) + 4, n;
int dec_size = lws_base64_size(len), n;
e->buf = lws_malloc(dec_size, "jwk");
if (!e->buf)
@ -215,9 +251,9 @@ _lws_jwk_set_element_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len)
}
static int
_lws_jwk_set_element_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len)
_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len)
{
int dec_size = ((len * 3) / 4) + 4, n;
int dec_size = lws_base64_size(len), n;
e->buf = lws_malloc(dec_size, "jwk");
if (!e->buf)
@ -239,26 +275,29 @@ lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m)
int n;
for (n = 0; n < m; n++)
if (el[n].buf)
if (el[n].buf) {
/* wipe all key material when it goes out of scope */
lws_explicit_bzero(el[n].buf, el[n].len);
lws_free_set_NULL(el[n].buf);
}
}
LWS_VISIBLE void
lws_jwk_destroy(struct lws_jwk *s)
lws_jwk_destroy(struct lws_jwk *jwk)
{
lws_jwk_destroy_elements(s->e, LWS_ARRAY_SIZE(s->e));
lws_jwk_destroy_elements(s->meta, LWS_ARRAY_SIZE(s->meta));
lws_jwk_destroy_elements(jwk->e, LWS_ARRAY_SIZE(jwk->e));
lws_jwk_destroy_elements(jwk->meta, LWS_ARRAY_SIZE(jwk->meta));
}
static signed char
cb_jwk(struct lejp_ctx *ctx, char reason)
{
struct cb_lws_jwk *cbs = (struct cb_lws_jwk *)ctx->user;
struct lws_jwk *s = cbs->s;
unsigned int idx, poss;
struct lws_jwk_parse_state *jps = (struct lws_jwk_parse_state *)ctx->user;
struct lws_jwk *jwk = jps->jwk;
unsigned int idx, poss, n;
if (reason == LEJPCB_VAL_STR_START)
cbs->pos = 0;
jps->pos = 0;
if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1)
/*
@ -269,12 +308,12 @@ cb_jwk(struct lejp_ctx *ctx, char reason)
* ACME specifies the keys must be ordered in lexographic
* order - where kty is not first.
*/
cbs->possible = F_RSA | F_EC | F_OCT;
jps->possible = F_RSA | F_EC | F_OCT;
if (reason == LEJPCB_OBJECT_END && ctx->path_match == 0 + 1) {
/* we completed parsing a key */
if (cbs->per_key_cb && cbs->possible) {
if (cbs->per_key_cb(cbs->s, cbs->user)) {
if (jps->per_key_cb && jps->possible) {
if (jps->per_key_cb(jps->jwk, jps->user)) {
lwsl_notice("%s: user cb halts import\n",
__func__);
@ -283,11 +322,99 @@ cb_jwk(struct lejp_ctx *ctx, char reason)
}
/* clear it down */
lws_jwk_destroy(cbs->s);
cbs->possible = 0;
lws_jwk_destroy(jps->jwk);
jps->possible = 0;
}
}
if (reason == LEJPCB_COMPLETE) {
/*
* Now we saw the whole jwk and know the key type, let'jwk insist
* that as a whole, it must be consistent and complete.
*
* The tracking of ->possible bits from even before we know the
* kty already makes certain we cannot have key element members
* defined that are inconsistent with the key type.
*/
for (n = 0; n < LWS_ARRAY_SIZE(tok_map); n++)
/*
* All mandataory elements for the key type
* must be present
*/
if ((tok_map[n] & jps->possible) && (
((tok_map[n] & (F_M | F_META)) == (F_M | F_META) &&
!jwk->meta[tok_map[n] & 0xff].buf) ||
((tok_map[n] & (F_M | F_META)) == F_M &&
!jwk->e[tok_map[n] & 0xff].buf))) {
lwsl_notice("%s: missing %s\n", __func__,
jwk_tok[n]);
return -3;
}
/*
* When the key may be public or public + private, ensure the
* intra-key members related to that are consistent.
*
* Only RSA keys need extra care, since EC keys are already
* confirmed by making CRV, X and Y mandatory and only D
* (the singular private part) optional. For RSA, N and E are
* also already known to be present using mandatory checking.
*/
/*
* If a private key, it must have all D, P and Q. Public key
* must have none of them.
*/
if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
!(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf)) ||
(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf))
) {
lwsl_notice("%s: RSA requires D, P and Q for private\n",
__func__);
return -3;
}
/*
* If the precomputed private key terms appear, they must all
* appear together.
*/
if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
!(((!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) &&
(!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf)) ||
(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DP].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_QI].buf))
) {
lwsl_notice("%s: RSA DP, DQ, QI must all appear "
"or none\n", __func__);
return -3;
}
/*
* The precomputed private key terms must not appear without
* the private key itself also appearing.
*/
if (jwk->kty == LWS_GENCRYPTO_KTY_RSA &&
!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_DQ].buf) {
lwsl_notice("%s: RSA DP, DQ, QI can appear only with "
"private key\n", __func__);
return -3;
}
if ((jwk->kty == LWS_GENCRYPTO_KTY_RSA ||
jwk->kty == LWS_GENCRYPTO_KTY_EC) &&
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf)
jwk->private_key = 1;
}
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
@ -301,43 +428,44 @@ cb_jwk(struct lejp_ctx *ctx, char reason)
switch (idx) {
/* note: kty is not necessarily first... we have to keep track of
* what could match given which element names have already been
* seen. Once kty comes, we confirm it's still possible (ie, it's
* not trying to tell us that it's RSA when we saw a "crv"
* already) and then reduce the possibilities to just the one that
* seen. Once kty comes, we confirm it'jwk still possible (ie, it'jwk
* not trying to tell us that it'jwk RSA now when we saw a "crv"
* earlier) and then reduce the possibilities to just the one that
* kty told. */
case F_RSA | F_EC | F_OCT | F_META | JWK_META_KTY:
case F_RSA | F_EC | F_OCT | F_META | F_M | JWK_META_KTY:
if (!strcmp(ctx->buf, "oct")) {
if (!(cbs->possible & F_OCT))
if (ctx->npos == 3 && !strncmp(ctx->buf, "oct", 3)) {
if (!(jps->possible & F_OCT))
goto elements_mismatch;
s->kty = LWS_GENCRYPTO_KYT_OCT;
cbs->possible = F_OCT;
jwk->kty = LWS_GENCRYPTO_KTY_OCT;
jps->possible = F_OCT;
goto cont;
}
if (!strcmp(ctx->buf, "RSA")) {
if (!(cbs->possible & F_RSA))
if (ctx->npos == 3 && !strncmp(ctx->buf, "RSA", 3)) {
if (!(jps->possible & F_RSA))
goto elements_mismatch;
s->kty = LWS_GENCRYPTO_KYT_RSA;
cbs->possible = F_RSA;
jwk->kty = LWS_GENCRYPTO_KTY_RSA;
jps->possible = F_RSA;
goto cont;
}
if (!strcmp(ctx->buf, "EC")) {
if (!(cbs->possible & F_EC))
if (ctx->npos == 2 && !strncmp(ctx->buf, "EC", 2)) {
if (!(jps->possible & F_EC))
goto elements_mismatch;
s->kty = LWS_GENCRYPTO_KYT_EC;
cbs->possible = F_EC;
jwk->kty = LWS_GENCRYPTO_KTY_EC;
jps->possible = F_EC;
goto cont;
}
lwsl_err("%s: Unknown KTY '%s'\n", __func__, ctx->buf);
lwsl_err("%s: Unknown KTY '%.*s'\n", __func__, ctx->npos,
ctx->buf);
return -1;
default:
cont:
if (cbs->pos + ctx->npos >= cbs->b64max)
if (jps->pos + ctx->npos >= (int)sizeof(jps->b64))
goto bail;
memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
cbs->pos += ctx->npos;
memcpy(jps->b64 + jps->pos, ctx->buf, ctx->npos);
jps->pos += ctx->npos;
if (reason == LEJPCB_VAL_STR_CHUNK)
return 0;
@ -345,13 +473,13 @@ cont:
/* chunking has been collated */
poss = idx & (F_RSA | F_EC | F_OCT);
cbs->possible &= poss;
if (!cbs->possible)
jps->possible &= poss;
if (!jps->possible)
goto elements_mismatch;
if (idx & F_META) {
if (_lws_jwk_set_element_jwk(&s->meta[idx & 0x7f],
cbs->b64, cbs->pos) < 0)
if (_lws_jwk_set_el_jwk(&jwk->meta[idx & 0x7f],
jps->b64, jps->pos) < 0)
goto bail;
break;
@ -359,24 +487,30 @@ cont:
if (idx & F_B64U) {
/* key data... do the base64 decode as needed */
if (_lws_jwk_set_element_jwk_b64u(&s->e[idx & 0x7f],
cbs->b64, cbs->pos)
< 0)
if (_lws_jwk_set_el_jwk_b64u(&jwk->e[idx & 0x7f],
jps->b64, jps->pos) < 0)
goto bail;
if (jwk->e[idx & 0x7f].len >
LWS_JWE_LIMIT_KEY_ELEMENT_BYTES) {
lwsl_notice("%s: oversize keydata\n", __func__);
goto bail;
}
return 0;
}
if (idx & F_B64) {
/* cert data... do non-urlcoded base64 decode */
if (_lws_jwk_set_element_jwk_b64(&s->e[idx & 0x7f],
cbs->b64, cbs->pos)
< 0)
if (_lws_jwk_set_el_jwk_b64(&jwk->e[idx & 0x7f],
jps->b64, jps->pos) < 0)
goto bail;
return 0;
}
if (_lws_jwk_set_element_jwk(&s->e[idx & 0x7f],
cbs->b64, cbs->pos) < 0)
if (_lws_jwk_set_el_jwk(&jwk->e[idx & 0x7f],
jps->b64, jps->pos) < 0)
goto bail;
break;
}
@ -392,140 +526,265 @@ bail:
return -1;
}
LWS_VISIBLE int
lws_jwk_import(struct lws_jwk *s, lws_jwk_key_import_callback cb, void *user,
const char *in, size_t len)
void
lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps,
struct lws_jwk *jwk, lws_jwk_key_import_callback cb,
void *user)
{
struct lejp_ctx jctx;
struct cb_lws_jwk cbs;
const int b64max = (((8192 / 8) * 4) / 3) + 1; /* enough for 8K key */
const char * const *tok = jwk_outer_tok;
char b64[b64max];
int m;
if (jwk)
memset(jwk, 0, sizeof(*jwk));
memset(s, 0, sizeof(*s));
cbs.s = s;
cbs.b64 = b64;
cbs.b64max = b64max;
cbs.pos = 0;
cbs.per_key_cb = cb;
cbs.user = user;
cbs.possible = F_RSA | F_EC | F_OCT;
jps->jwk = jwk;
jps->possible = F_RSA | F_EC | F_OCT;
jps->per_key_cb = cb;
jps->user = user;
jps->pos = 0;
if (cb == NULL)
tok = jwk_tok;
lejp_construct(jctx, cb_jwk, jps, cb ? jwk_outer_tok: jwk_tok,
LWS_ARRAY_SIZE(jwk_tok));
}
lejp_construct(&jctx, cb_jwk, &cbs, tok, LWS_ARRAY_SIZE(jwk_tok));
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
lejp_destruct(&jctx);
LWS_VISIBLE int
lws_jwk_generate(struct lws_context *context, struct lws_jwk *jwk,
enum lws_gencrypto_kty kty, int bits, const char *curve)
{
int n;
if (m < 0) {
lwsl_notice("%s: parse got %d\n", __func__, m);
memset(jwk, 0, sizeof(*jwk));
return -1;
jwk->kty = kty;
jwk->private_key = 1;
switch (kty) {
case LWS_GENCRYPTO_KTY_RSA:
{
struct lws_genrsa_ctx ctx;
lwsl_notice("%s: generating %d bit RSA key\n", __func__, bits);
n = lws_genrsa_new_keypair(context, &ctx, LGRSAM_PKCS1_1_5,
jwk->e, bits);
lws_genrsa_destroy(&ctx);
if (n) {
lwsl_err("%s: problem generating RSA key\n", __func__);
return 1;
}
}
break;
case LWS_GENCRYPTO_KTY_OCT:
n = lws_gencrypto_bits_to_bytes(bits);
jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(n, "oct");
jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = n;
if (lws_get_random(context,
jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf, n) != n) {
lwsl_err("%s: problem getting random\n", __func__);
return 1;
}
break;
case LWS_GENCRYPTO_KTY_EC:
{
struct lws_genec_ctx ctx;
if (s->kty == LWS_GENCRYPTO_KYT_UNKNOWN) {
lwsl_notice("%s: missing or unknown kyt\n", __func__);
return -1;
if (!curve) {
lwsl_err("%s: must have a named curve\n", __func__);
return 1;
}
if (lws_genecdsa_create(&ctx, context, NULL))
return 1;
lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__,
curve);
n = lws_genecdsa_new_keypair(&ctx, curve, jwk->e);
lws_genec_destroy(&ctx);
if (n) {
lwsl_err("%s: problem generating ECDSA key\n", __func__);
return 1;
}
}
break;
case LWS_GENCRYPTO_KTY_UNKNOWN:
default:
lwsl_err("%s: unknown kty\n", __func__);
return 1;
}
return 0;
}
LWS_VISIBLE int
lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
lws_jwk_import(struct lws_jwk *jwk, lws_jwk_key_import_callback cb, void *user,
const char *in, size_t len)
{
char *start = p, *end = &p[len - 1];
int n, limit = LWS_COUNT_JWK_ELEMENTS;
struct lejp_ctx jctx;
struct lws_jwk_parse_state jps;
int m;
/* RFC7638 lexicographic order requires
* RSA: e -> kty -> n
* oct: k -> kty
*/
lws_jwk_init_jps(&jctx, &jps, jwk, cb, user);
p += lws_snprintf(p, end - p, "{");
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
lejp_destruct(&jctx);
switch (s->kty) {
case LWS_GENCRYPTO_KYT_OCT:
if (!s->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf)
return -1;
p += lws_snprintf(p, end - p, "\"k\":\"");
n = lws_jws_base64_enc((const char *)s->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf,
s->e[LWS_GENCRYPTO_OCT_KEYEL_K].len, p, end - p - 4);
if (n < 0) {
lwsl_notice("%s: enc failed\n", __func__);
return -1;
}
p += n;
p += lws_snprintf(p, end - p, "\",\"kty\":\"%s\"}",
kyt_names[s->kty]);
return p - start;
case LWS_GENCRYPTO_KYT_RSA:
if (!s->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf ||
!s->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf ||
(private && (!s->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf ||
!s->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf ||
!s->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf))
) {
lwsl_notice("%s: not enough elements filled\n",
__func__);
return -1;
}
if (!private)
limit = LWS_GENCRYPTO_RSA_KEYEL_N + 1;
for (n = 0; n < limit; n++) {
int m;
if (!s->e[n].buf)
continue;
lwsl_info("%d: len %d\n", n, s->e[n].len);
if (n)
p += lws_snprintf(p, end - p, ",");
p += lws_snprintf(p, end - p, "\"%s\":\"", jwk_tok[n]);
m = lws_jws_base64_enc((const char *)s->e[n].buf,
s->e[n].len, p,
end - p - 4);
if (m < 0) {
lwsl_notice("%s: enc fail inlen %d outlen %d\n",
__func__, (int)s->e[n].len,
lws_ptr_diff(end, p) - 4);
return -1;
}
p += m;
*p++ = '\"';
if (!n) /* RFC7638 lexicographic order */
p += lws_snprintf(p, end - p, ",\"kty\":\"%s\"",
kyt_names[s->kty]);
}
p += lws_snprintf(p, end - p, "}");
return p - start;
case LWS_GENCRYPTO_KYT_EC:
return p - start;
if (m < 0) {
lwsl_notice("%s: parse got %d\n", __func__, m);
lws_jwk_destroy(jwk);
return -1;
}
switch (jwk->kty) {
case LWS_GENCRYPTO_KTY_UNKNOWN:
lwsl_notice("%s: missing or unknown kyt\n", __func__);
lws_jwk_destroy(jwk);
return -1;
default:
break;
}
lwsl_err("%s: unknown key type %d\n", __func__, s->kty);
return 0;
}
return -1;
LWS_VISIBLE int
lws_jwk_export(struct lws_jwk *jwk, int private, char *p, size_t len)
{
char *start = p, *end = &p[len - 1];
int n, m, limit, first = 1, asym = 0;
struct lexico *l;
/* RFC7638 lexicographic order requires
* RSA: e -> kty -> n
* oct: k -> kty
*
* ie, meta and key data elements appear interleaved in name alpha order
*/
p += lws_snprintf(p, end - p, "{");
switch (jwk->kty) {
case LWS_GENCRYPTO_KTY_OCT:
l = lexico_oct;
limit = LWS_ARRAY_SIZE(lexico_oct);
break;
case LWS_GENCRYPTO_KTY_RSA:
l = lexico_rsa;
limit = LWS_ARRAY_SIZE(lexico_rsa);
asym = 1;
break;
case LWS_GENCRYPTO_KTY_EC:
l = lexico_ec;
limit = LWS_ARRAY_SIZE(lexico_ec);
asym = 1;
break;
default:
return -1;
}
for (n = 0; n < limit; n++) {
const char *q, *q_end;
char tok[12];
int pos = 0, f = 1;
if ((l->meta & 1) && (jwk->meta[l->idx].buf ||
l->idx == (int)JWK_META_KTY)) {
switch (l->idx) {
case JWK_META_KTY:
if (!first)
*p++ = ',';
first = 0;
p += lws_snprintf(p, end - p, "\"%s\":\"%s\"",
l->name, kty_names[jwk->kty]);
break;
case JWK_META_KEY_OPS:
if (!first)
*p++ = ',';
first = 0;
q = (const char *)jwk->meta[l->idx].buf;
q_end = q + jwk->meta[l->idx].len;
p += lws_snprintf(p, end - p,
"\"%s\":[", l->name);
/*
* For the public version, usages that
* require the private part must be
* snipped
*/
while (q < q_end) {
if (*q != ' ' && pos < (int)sizeof(tok) - 1) {
tok[pos++] = *q++;
if (q != q_end)
continue;
}
tok[pos] = '\0';
pos = 0;
if (private || !asym ||
(strcmp(tok, "sign") &&
strcmp(tok, "encrypt"))) {
if (!f)
*p++ = ',';
f = 0;
p += lws_snprintf(p, end - p,
"\"%s\"", tok);
}
q++;
}
*p++ = ']';
break;
default:
/* both sig and enc require asym private key */
if (!private && asym && l->idx == (int)JWK_META_USE)
break;
if (!first)
*p++ = ',';
first = 0;
p += lws_snprintf(p, end - p, "\"%s\":\"%.*s\"",
l->name, jwk->meta[l->idx].len,
jwk->meta[l->idx].buf);
break;
}
}
if ((!(l->meta & 1)) && jwk->e[l->idx].buf &&
(private || !(l->meta & 2))) {
if (!first)
*p++ = ',';
first = 0;
p += lws_snprintf(p, end - p, "\"%s\":\"", l->name);
if (jwk->kty == LWS_GENCRYPTO_KTY_EC &&
l->idx == (int)LWS_GENCRYPTO_EC_KEYEL_CRV)
m = lws_snprintf(p, end - p, "%.*s",
jwk->e[l->idx].len,
(const char *)jwk->e[l->idx].buf);
else
m = lws_jws_base64_enc(
(const char *)jwk->e[l->idx].buf,
jwk->e[l->idx].len, p, end - p - 4);
if (m < 0) {
lwsl_notice("%s: enc failed\n", __func__);
return -1;
}
p += m;
p += lws_snprintf(p, end - p, "\"");
}
l++;
}
p += lws_snprintf(p, end - p, "}\n");
return p - start;
}
LWS_VISIBLE int
lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
lws_jwk_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32)
{
struct lws_genhash_ctx hash_ctx;
int tmpsize = 2536, n;
@ -533,7 +792,7 @@ lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
tmp = lws_malloc(tmpsize, "rfc7638 tmp");
n = lws_jwk_export(s, 0, tmp, tmpsize);
n = lws_jwk_export(jwk, 0, tmp, tmpsize);
if (n < 0)
goto bail;
@ -559,7 +818,20 @@ bail:
}
LWS_VISIBLE int
lws_jwk_load(struct lws_jwk *s, const char *filename,
lws_jwk_strdup_meta(struct lws_jwk *jwk, enum enum_jwk_meta_tok idx,
const char *in, int len)
{
jwk->meta[idx].buf = lws_malloc(len, __func__);
if (!jwk->meta[idx].buf)
return 1;
jwk->meta[idx].len = len;
memcpy(jwk->meta[idx].buf, in, len);
return 0;
}
LWS_VISIBLE int
lws_jwk_load(struct lws_jwk *jwk, const char *filename,
lws_jwk_key_import_callback cb, void *user)
{
int buflen = 4096;
@ -573,7 +845,7 @@ lws_jwk_load(struct lws_jwk *s, const char *filename,
if (n < 0)
goto bail;
n = lws_jwk_import(s, cb, user, buf, n);
n = lws_jwk_import(jwk, cb, user, buf, n);
lws_free(buf);
return n;
@ -584,7 +856,7 @@ bail:
}
LWS_VISIBLE int
lws_jwk_save(struct lws_jwk *s, const char *filename)
lws_jwk_save(struct lws_jwk *jwk, const char *filename)
{
int buflen = 4096;
char *buf = lws_malloc(buflen, "jwk-save");
@ -593,7 +865,7 @@ lws_jwk_save(struct lws_jwk *s, const char *filename)
if (!buf)
return -1;
n = lws_jwk_export(s, 1, buf, buflen);
n = lws_jwk_export(jwk, 1, buf, buflen);
if (n < 0)
goto bail;

View file

@ -23,6 +23,7 @@
*/
#include "core/private.h"
#include "jose/private.h"
#include <stdint.h>
@ -54,8 +55,11 @@ static const char * const jws_jose[] = {
};
struct jose_cb_args {
const struct lws_jose_jwe_alg **args;
const struct lws_jose_jwe_alg **enc_args; /* null for jws case */
struct lws_jose *jose;
struct lejp_ctx jwk_jctx; /* fake lejp context used to parse epk */
struct lws_jwk_parse_state jps; /* fake jwk parse state */
char *temp;
int *temp_len;
int is_jwe;
};
@ -63,6 +67,28 @@ static signed char
lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
{
struct jose_cb_args *args = (struct jose_cb_args *)ctx->user;
int n;
/*
* In JOSE JSON, the element "epk" contains a fully-formed JWK.
*
* For JOSE paths beginning "epk.", we pass them through to a JWK
* LEJP subcontext to parse using the JWK parser directly.
*/
if (args->is_jwe && !strncmp(ctx->path, "epk.", 4)) {
memcpy(args->jwk_jctx.path, ctx->path + 4,
sizeof(ctx->path) - 4);
memcpy(args->jwk_jctx.buf, ctx->buf, ctx->npos);
args->jwk_jctx.npos = ctx->npos;
if (!ctx->path_match)
args->jwk_jctx.path_match = 0;
lejp_check_path_match(&args->jwk_jctx);
if (args->jwk_jctx.path_match)
args->jwk_jctx.callback(&args->jwk_jctx, reason);
}
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
@ -79,7 +105,8 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
*/
if (!args->is_jwe &&
lws_gencrypto_jws_alg_to_definition(ctx->buf, args->args)) {
lws_gencrypto_jws_alg_to_definition(ctx->buf,
&args->jose->alg)) {
lwsl_notice("%s: unknown alg '%s'\n", __func__,
ctx->buf);
@ -87,8 +114,9 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
}
if (args->is_jwe &&
lws_gencrypto_jwe_alg_to_definition(ctx->buf, args->args)) {
lwsl_notice("%s: unknown alg '%s'\n", __func__,
lws_gencrypto_jwe_alg_to_definition(ctx->buf,
&args->jose->alg)) {
lwsl_notice("%s: unknown JWE alg '%s'\n", __func__,
ctx->buf);
return -1;
@ -127,11 +155,13 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
/* past here, JWE only */
case LJJHI_ENC: /* JWE only: Optional: string */
if (!args->is_jwe)
case LJJHI_ENC: /* JWE only: Mandatory: string */
if (!args->is_jwe) {
lwsl_info("%s: enc in jws\n", __func__);
return -1;
}
if (lws_gencrypto_jwe_enc_to_definition(ctx->buf,
args->enc_args)) {
&args->jose->enc_alg)) {
lwsl_notice("%s: unknown enc '%s'\n", __func__,
ctx->buf);
@ -142,41 +172,45 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
case LJJHI_ZIP: /* JWE only: Optional: string ("DEF" = deflate) */
if (!args->is_jwe)
return -1;
break;
goto append_string;
case LJJHI_EPK: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
/* Ephemeral key... this JSON subsection is actually a JWK */
lwsl_err("LJJHI_EPK\n");
break;
case LJJHI_APU: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
break;
/* Agreement Party U */
goto append_string;
case LJJHI_APV: /* Additional arg for JWE ECDH */
if (!args->is_jwe)
return -1;
break;
/* Agreement Party V */
goto append_string;
case LJJHI_IV: /* Additional arg for JWE AES */
if (!args->is_jwe)
return -1;
break;
goto append_string;
case LJJHI_TAG: /* Additional arg for JWE AES */
if (!args->is_jwe)
return -1;
break;
goto append_string;
case LJJHI_P2S: /* Additional arg for JWE PBES2 */
if (!args->is_jwe)
return -1;
break;
goto append_string;
case LJJHI_P2C: /* Additional arg for JWE PBES2 */
if (!args->is_jwe)
return -1;
break;
goto append_string;
/* ignore what we don't understand */
@ -185,21 +219,76 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
}
return 0;
append_string:
if (*args->temp_len < ctx->npos) {
lwsl_err("%s: out of parsing space\n", __func__);
return -1;
}
if (!args->jose->e[ctx->path_match - 1].buf) {
args->jose->e[ctx->path_match - 1].buf = (uint8_t *)args->temp;
args->jose->e[ctx->path_match - 1].len = 0;
}
memcpy(args->temp, ctx->buf, ctx->npos);
args->temp += ctx->npos;
*args->temp_len -= ctx->npos;
args->jose->e[ctx->path_match - 1].len += ctx->npos;
if (reason == LEJPCB_VAL_STR_END) {
n = lws_b64_decode_string_len(
(const char *)args->jose->e[ctx->path_match - 1].buf,
args->jose->e[ctx->path_match - 1].len,
(char *)args->jose->e[ctx->path_match - 1].buf,
args->jose->e[ctx->path_match - 1].len + 1);
if (n < 0) {
lwsl_err("%s: b64 decode failed\n", __func__);
return -1;
}
args->temp -= args->jose->e[ctx->path_match - 1].len - n - 1;
*args->temp_len +=
args->jose->e[ctx->path_match - 1].len - n - 1;
args->jose->e[ctx->path_match - 1].len = n;
}
return 0;
}
void
lws_jose_init(struct lws_jose *jose)
{
memset(jose, 0, sizeof(*jose));
}
void
lws_jose_destroy(struct lws_jose *jose)
{
// lws_gencrypto_destroy_elements(jose->e, LWS_ARRAY_SIZE(jose->e));
lws_jwk_destroy(&jose->jwk_ephemeral);
}
static int
lws_jose_parse(const struct lws_jose_jwe_alg **_args,
const struct lws_jose_jwe_alg **enc_args,
uint8_t *buf, int n, int is_jwe)
lws_jose_parse(struct lws_jose *jose, const uint8_t *buf, int n,
char *temp, int *temp_len, int is_jwe)
{
struct lejp_ctx jctx;
struct jose_cb_args args;
int m;
args.args = _args;
args.enc_args = enc_args;
if (is_jwe)
/* prepare a context for JOSE epk ephemeral jwk parsing */
lws_jwk_init_jps(&args.jwk_jctx, &args.jps,
&jose->jwk_ephemeral, NULL, NULL);
args.is_jwe = is_jwe;
args.temp = temp;
args.temp_len = temp_len;
args.jose = jose;
lejp_construct(&jctx, lws_jws_jose_cb, &args, jws_jose,
LWS_ARRAY_SIZE(jws_jose));
@ -207,7 +296,7 @@ lws_jose_parse(const struct lws_jose_jwe_alg **_args,
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
lejp_destruct(&jctx);
if (m < 0) {
lwsl_notice("parse got %d\n", m);
lwsl_notice("%s: parse %.*s returned %d\n", __func__, n, buf, m);
return -1;
}
@ -215,16 +304,17 @@ lws_jose_parse(const struct lws_jose_jwe_alg **_args,
}
int
lws_jws_parse_jose(const struct lws_jose_jwe_alg **args,
uint8_t *buf, int n)
lws_jws_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len)
{
return lws_jose_parse(args, NULL, buf, n, 0);
return lws_jose_parse(jose, (const uint8_t *)buf, len,
temp, temp_len, 0);
}
int
lws_jwe_parse_jose(const struct lws_jose_jwe_alg **args,
const struct lws_jose_jwe_alg **enc_args,
uint8_t *buf, int n)
lws_jwe_parse_jose(struct lws_jose *jose,
const char *buf, int len, char *temp, int *temp_len)
{
return lws_jose_parse(args, enc_args, buf, n, 1);
return lws_jose_parse(jose,
(const uint8_t *)buf, len, temp, temp_len, 1);
}

View file

@ -22,14 +22,129 @@
#include "core/private.h"
#include "private.h"
LWS_VISIBLE void
lws_jws_init(struct lws_jws *jws, struct lws_jwk *jwk,
struct lws_context *context)
{
memset(jws, 0, sizeof(*jws));
jws->context = context;
jws->jwk = jwk;
}
static void
lws_jws_compact_map_bzero(struct lws_jws_compact_map *map)
{
int n;
/* no need to scrub first jose header element (it can be canned then) */
for (n = 1; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++)
if (map->buf[n])
lws_explicit_bzero((void *)map->buf[n], map->len[n]);
}
LWS_VISIBLE void
lws_jws_destroy(struct lws_jws *jws)
{
lws_jws_compact_map_bzero(&jws->map);
jws->jwk = NULL;
}
LWS_VISIBLE int
lws_jws_dup_element(struct lws_jws_compact_map *map, int idx,
char *temp, int *temp_len, const void *in, size_t in_len,
size_t actual_alloc)
{
if (!actual_alloc)
actual_alloc = in_len;
if ((size_t)*temp_len < actual_alloc)
return -1;
map->len[idx] = in_len;
map->buf[idx] = temp;
memcpy((void *)map->buf[idx], in, in_len);
*temp_len -= actual_alloc;
return 0;
}
LWS_VISIBLE int
lws_jws_encode_b64_element(struct lws_jws_compact_map *map, int idx,
char *temp, int *temp_len, const void *in,
size_t in_len)
{
int n;
if (*temp_len < lws_base64_size((int)in_len))
return -1;
n = lws_jws_base64_enc(in, in_len, temp, *temp_len);
if (n < 0)
return -1;
map->len[idx] = n;
map->buf[idx] = temp;
*temp_len -= n;
return 0;
}
LWS_VISIBLE int
lws_jws_randomize_element(struct lws_context *context,
struct lws_jws_compact_map *map,
int idx, char *temp, int *temp_len, size_t random_len,
size_t actual_alloc)
{
if (!actual_alloc)
actual_alloc = random_len;
if ((size_t)*temp_len < actual_alloc)
return -1;
map->len[idx] = random_len;
map->buf[idx] = temp;
if (lws_get_random(context, temp, random_len) != (int)random_len) {
lwsl_err("Problem getting random\n");
return -1;
}
*temp_len -= actual_alloc;
return 0;
}
LWS_VISIBLE int
lws_jws_alloc_element(struct lws_jws_compact_map *map, int idx, char *temp,
int *temp_len, size_t len, size_t actual_alloc)
{
if (!actual_alloc)
actual_alloc = len;
if ((size_t)*temp_len < actual_alloc)
return -1;
map->len[idx] = len;
map->buf[idx] = temp;
*temp_len -= actual_alloc;
return 0;
}
LWS_VISIBLE int
lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max)
{
int n;
n = lws_b64_encode_string_url(in, in_len, out, out_max - 1);
if (n < 0)
if (n < 0) {
lwsl_notice("%s: in len %d too large for %d out buf\n",
__func__, (int)in_len, (int)out_max);
return n; /* too large for output buffer */
}
/* trim the terminal = */
while (n && out[n - 1] == '=')
@ -40,6 +155,99 @@ lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max)
return n;
}
LWS_VISIBLE int
lws_jws_b64_compact_map(const char *in, int len, struct lws_jws_compact_map *map)
{
int me = 0;
memset(map, 0, sizeof(*map));
map->buf[me] = (char *)in;
map->len[me] = 0;
while (len--) {
if (*in++ == '.') {
if (++me == LWS_JWS_MAX_COMPACT_BLOCKS)
return -1;
map->buf[me] = (char *)in;
map->len[me] = 0;
continue;
}
map->len[me]++;
}
return me + 1;
}
/* b64 in, map contains decoded elements, if non-NULL,
* map_b64 set to b64 elements
*/
LWS_VISIBLE int
lws_jws_compact_decode(const char *in, int len, struct lws_jws_compact_map *map,
struct lws_jws_compact_map *map_b64, char *out,
int *out_len)
{
int blocks, n, m = 0;
if (!map_b64)
map_b64 = map;
memset(map_b64, 0, sizeof(*map_b64));
memset(map, 0, sizeof(*map));
blocks = lws_jws_b64_compact_map(in, len, map_b64);
if (blocks > LWS_JWS_MAX_COMPACT_BLOCKS)
return -1;
while (m < blocks) {
n = lws_b64_decode_string_len(map_b64->buf[m],
map_b64->len[m], out, *out_len);
if (n < 0) {
lwsl_err("%s: b64 decode failed\n", __func__);
return -1;
}
/* replace the map entry with the decoded content */
map->buf[m] = out;
map->len[m++] = n;
out += n;
*out_len -= n;
if (*out_len < 1)
return -1;
}
return blocks;
}
static int
lws_jws_compact_decode_map(struct lws_jws_compact_map *map_b64,
struct lws_jws_compact_map *map, char *out,
int *out_len)
{
int n, m = 0;
for (n = 0; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++) {
n = lws_b64_decode_string_len(map_b64->buf[m],
map_b64->len[m], out, *out_len);
if (n < 0) {
lwsl_err("%s: b64 decode failed\n", __func__);
return -1;
}
/* replace the map entry with the decoded content */
map->buf[m] = out;
map->len[m++] = n;
out += n;
*out_len -= n;
if (*out_len < 1)
return -1;
}
return 0;
}
LWS_VISIBLE int
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
char *end)
@ -62,71 +270,81 @@ lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
return (*p) - p_entry;
}
static int
lws_jws_find_sig(const char *in, size_t len)
LWS_VISIBLE int
lws_jws_compact_encode(struct lws_jws_compact_map *map_b64, /* b64-encoded */
const struct lws_jws_compact_map *map, /* non-b64 */
char *buf, int *len)
{
const char *p = in + len - 1;
int n, m;
while (len--)
if (*p == '.')
return (p + 1) - in;
else
p--;
for (n = 0; n < LWS_JWS_MAX_COMPACT_BLOCKS; n++) {
if (!map->buf[n]) {
map_b64->buf[n] = NULL;
map_b64->len[n] = 0;
continue;
}
m = lws_jws_base64_enc(map->buf[n], map->len[n], buf, *len);
if (m < 0)
return -1;
buf += m;
*len -= m;
if (*len < 1)
return -1;
}
lwsl_notice("%s failed\n", __func__);
return -1;
return 0;
}
/*
* This takes both a base64 -encoded map and a plaintext map.
*
* JWS demands base-64 encoded elements for hash computation and at least for
* the JOSE header and signature, decoded versions too.
*/
LWS_VISIBLE int
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
struct lws_context *context)
lws_jws_sig_confirm(struct lws_jws_compact_map *map_b64, /* b64-encoded */
struct lws_jws_compact_map *map, /* non-b64 */
struct lws_jwk *jwk, struct lws_context *context)
{
int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
enum enum_genrsa_mode padding = LGRSAM_PKCS1_1_5;
char temp[256];
int n, h_len, b = 3, temp_len = sizeof(temp);
uint8_t digest[LWS_GENHASH_LARGEST];
const struct lws_jose_jwe_alg *args = NULL;
struct lws_genhash_ctx hash_ctx;
struct lws_genec_ctx ecdsactx;
struct lws_genrsa_ctx rsactx;
struct lws_genhmac_ctx ctx;
char buf[2048];
struct lws_jose jose;
/* 1) there has to be a signature */
lws_jose_init(&jose);
if (sig_pos < 0)
return -1;
/* only valid if no signature or key */
if (!map_b64->buf[LJWS_SIG] && !map->buf[LJWS_UHDR])
b = 2;
/* 2) find length of first, hdr, block */
while (pos < (int)len && in[pos] != '.')
pos++;
if (pos == (int)len)
return -1;
/* 3) Decode the header block */
n = lws_b64_decode_string_len(in, pos, buf, sizeof(buf) - 1);
if (n < 0)
return -1;
/* 4) Require either:
* typ: JWT (if present) and alg: HS256/384/512
* typ: JWT (if present) and alg: RS256/384/512
* typ: JWT (if present) and alg: ES256/384/512
*/
m = lws_jws_parse_jose(&args, (unsigned char *)buf, n);
if (m < 0) {
lwsl_notice("parse got %d: alg %s\n", m, args->alg);
if (lws_jws_parse_jose(&jose, map->buf[LJWS_JOSE], map->len[LJWS_JOSE],
temp, &temp_len) < 0) {
lwsl_notice("%s: parse failed\n", __func__);
return -1;
}
/* 5) decode the B64URL signature part into buf / m */
if (!strcmp(jose.alg->alg, "none")) {
/* "none" compact serialization has 2 blocks: jose.payload */
if (b != 2 || jwk)
return -1;
m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
buf, sizeof(buf) - 1);
/* the lack of a key matches the lack of a signature */
return 0;
}
switch (args->algtype_signing) {
/* all other have 3 blocks: jose.payload.sig */
if (b != 3 || !jwk) {
lwsl_notice("%s: %d blocks\n", __func__, b);
return -1;
}
switch (jose.alg->algtype_signing) {
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS:
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP:
padding = LGRSAM_PKCS1_OAEP_PSS;
@ -135,67 +353,87 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
/* RSASSA-PKCS1-v1_5 or OAEP using SHA-256/384/512 */
if (jwk->kty != LWS_GENCRYPTO_KYT_RSA)
if (jwk->kty != LWS_GENCRYPTO_KTY_RSA)
return -1;
/* 6(RSA): compute the hash of the payload into "digest" */
if (lws_genhash_init(&hash_ctx, args->hash_type))
if (lws_genhash_init(&hash_ctx, jose.alg->hash_type))
return -1;
if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
/*
* JWS Signing Input value:
*
* BASE64URL(UTF8(JWS Protected Header)) || '.' ||
* BASE64URL(JWS Payload)
*/
if (lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_JOSE],
map_b64->len[LJWS_JOSE]) ||
lws_genhash_update(&hash_ctx, ".", 1) ||
lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_PYLD],
map_b64->len[LJWS_PYLD]) ||
lws_genhash_destroy(&hash_ctx, digest)) {
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
if (lws_genhash_destroy(&hash_ctx, digest))
return -1;
h_len = lws_genhash_size(jose.alg->hash_type);
h_len = lws_genhash_size(args->hash_type);
if (lws_genrsa_create(&rsactx, jwk->e, context, padding)) {
if (lws_genrsa_create(&rsactx, jwk->e, context, padding,
LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = lws_genrsa_hash_sig_verify(&rsactx, digest, args->hash_type,
(uint8_t *)buf, m);
n = lws_genrsa_hash_sig_verify(&rsactx, digest,
jose.alg->hash_type,
(uint8_t *)map->buf[LJWS_SIG],
map->len[LJWS_SIG]);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_notice("decrypt fail\n");
lwsl_notice("%s: decrypt fail\n", __func__);
return -1;
}
break;
case LWS_JOSE_ENCTYPE_NONE:
case LWS_JOSE_ENCTYPE_NONE: /* HSxxx */
/* SHA256/384/512 HMAC */
h_len = lws_genhmac_size(args->hmac_type);
if (m < 0 || m != h_len)
return -1;
h_len = lws_genhmac_size(jose.alg->hmac_type);
/* 6) compute HMAC over payload */
if (lws_genhmac_init(&ctx, args->hmac_type,
if (lws_genhmac_init(&ctx, jose.alg->hmac_type,
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf,
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len))
return -1;
if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
/*
* JWS Signing Input value:
*
* BASE64URL(UTF8(JWS Protected Header)) || '.' ||
* BASE64URL(JWS Payload)
*/
if (lws_genhmac_update(&ctx, map_b64->buf[LJWS_JOSE],
map_b64->len[LJWS_JOSE]) ||
lws_genhmac_update(&ctx, ".", 1) ||
lws_genhmac_update(&ctx, map_b64->buf[LJWS_PYLD],
map_b64->len[LJWS_PYLD]) ||
lws_genhmac_destroy(&ctx, digest)) {
lws_genhmac_destroy(&ctx, NULL);
return -1;
}
if (lws_genhmac_destroy(&ctx, digest))
return -1;
/* 7) Compare the computed and decoded hashes */
if (memcmp(digest, buf, h_len)) {
if (lws_timingsafe_bcmp(digest, map->buf[2], h_len)) {
lwsl_notice("digest mismatch\n");
return -1;
@ -207,10 +445,10 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
/* ECDSA using SHA-256/384/512 */
/* the key coming in with this makes sense, right? */
/* Confirm the key coming in with this makes sense */
/* has to be an EC key :-) */
if (jwk->kty != LWS_GENCRYPTO_KYT_EC)
if (jwk->kty != LWS_GENCRYPTO_KTY_EC)
return -1;
/* key must state its curve */
@ -219,23 +457,41 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
/* key must match the selected alg curve */
if (strcmp((const char *)jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
args->curve_name))
jose.alg->curve_name))
return -1;
/* compute the hash of the payload into "digest" */
/*
* JWS Signing Input value:
*
* BASE64URL(UTF8(JWS Protected Header)) || '.' ||
* BASE64URL(JWS Payload)
*
* Validating the JWS Signature is a bit different from the
* previous examples. We need to split the 64 member octet
* sequence of the JWS Signature (which is base64url decoded
* from the value encoded in the JWS representation) into two
* 32 octet sequences, the first representing R and the second
* S. We then pass the public key (x, y), the signature (R, S),
* and the JWS Signing Input (which is the initial substring of
* the JWS Compact Serialization representation up until but not
* including the second period character) to an ECDSA signature
* verifier that has been configured to use the P-256 curve with
* the SHA-256 hash function.
*/
if (lws_genhash_init(&hash_ctx, args->hash_type))
return -1;
if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_JOSE],
map_b64->len[LJWS_JOSE]) ||
lws_genhash_update(&hash_ctx, ".", 1) ||
lws_genhash_update(&hash_ctx, map_b64->buf[LJWS_PYLD],
map_b64->len[LJWS_PYLD]) ||
lws_genhash_destroy(&hash_ctx, digest)) {
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
if (lws_genhash_destroy(&hash_ctx, digest))
return -1;
h_len = lws_genhash_size(args->hash_type);
h_len = lws_genhash_size(jose.alg->hash_type);
if (lws_genecdsa_create(&ecdsactx, context, NULL)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
@ -249,13 +505,14 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
return -1;
}
n = lws_genecdsa_hash_sig_verify(&ecdsactx, digest,
args->hash_type,
(uint8_t *)buf, m);
n = lws_genecdsa_hash_sig_verify_jws(&ecdsactx, digest,
jose.alg->hash_type,
jose.alg->keybits_fixed,
(uint8_t *)map->buf[LJWS_SIG],
map->len[LJWS_SIG]);
lws_genec_destroy(&ecdsactx);
if (n < 0) {
lwsl_notice("decrypt fail\n");
lwsl_notice("%s: verify fail\n", __func__);
return -1;
}
@ -269,60 +526,121 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
return 0;
}
/* it's already a b64 map, we will make a temp plain version */
LWS_VISIBLE int
lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
size_t pay_len, char *b64_sig, size_t sig_len,
const struct lws_jose_jwe_alg *args,
struct lws_jwk *jwk,
struct lws_context *context)
lws_jws_sig_confirm_compact_b64_map(struct lws_jws_compact_map *map_b64,
struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len)
{
enum enum_genrsa_mode padding = LGRSAM_PKCS1_1_5;
struct lws_jws_compact_map map;
int n;
n = lws_jws_compact_decode_map(map_b64, &map, temp, temp_len);
if (n > 3 || n < 0)
return -1;
return lws_jws_sig_confirm(map_b64, &map, jwk, context);
}
/*
* it's already a compact / concatenated b64 string, we will make a temp
* plain version
*/
LWS_VISIBLE int
lws_jws_sig_confirm_compact_b64(const char *in, size_t len,
struct lws_jws_compact_map *map,
struct lws_jwk *jwk,
struct lws_context *context,
char *temp, int *temp_len)
{
struct lws_jws_compact_map map_b64;
int n;
if (lws_jws_b64_compact_map(in, len, &map_b64) < 0)
return -1;
n = lws_jws_compact_decode(in, len, map, &map_b64, temp, temp_len);
if (n > 3 || n < 0)
return -1;
return lws_jws_sig_confirm(&map_b64, map, jwk, context);
}
/* it's already plain, we will make a temp b64 version */
LWS_VISIBLE int
lws_jws_sig_confirm_compact(struct lws_jws_compact_map *map, struct lws_jwk *jwk,
struct lws_context *context, char *temp,
int *temp_len)
{
struct lws_jws_compact_map map_b64;
if (lws_jws_compact_encode(&map_b64, map, temp, temp_len) < 0)
return -1;
return lws_jws_sig_confirm(&map_b64, map, jwk, context);
}
LWS_VISIBLE int
lws_jws_sign_from_b64(struct lws_jose *jose, struct lws_jws *jws,
char *b64_sig, size_t sig_len)
{
enum enum_genrsa_mode pad = LGRSAM_PKCS1_1_5;
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_genhash_ctx hash_ctx;
struct lws_genec_ctx ecdsactx;
struct lws_genrsa_ctx rsactx;
int n, m;
uint8_t *buf;
int n, m;
if (jose->alg->hash_type == LWS_GENHASH_TYPE_UNKNOWN &&
jose->alg->hmac_type == LWS_GENHMAC_TYPE_UNKNOWN &&
!strcmp(jose->alg->alg, "none"))
return 0;
if (lws_genhash_init(&hash_ctx, jose->alg->hash_type) ||
lws_genhash_update(&hash_ctx, jws->map_b64.buf[LJWS_JOSE],
jws->map_b64.len[LJWS_JOSE]) ||
lws_genhash_update(&hash_ctx, ".", 1) ||
lws_genhash_update(&hash_ctx, jws->map_b64.buf[LJWS_PYLD],
jws->map_b64.len[LJWS_PYLD]) ||
lws_genhash_destroy(&hash_ctx, digest)) {
lws_genhash_destroy(&hash_ctx, NULL);
if (lws_genhash_init(&hash_ctx, args->hash_type))
return -1;
if (b64_hdr) {
if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_hdr, hdr_len))
goto hash_fail;
if (lws_genhash_update(&hash_ctx, (uint8_t *)".", 1))
goto hash_fail;
}
if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_pay, pay_len))
goto hash_fail;
if (lws_genhash_destroy(&hash_ctx, digest))
return -1;
switch (args->algtype_signing) {
switch (jose->alg->algtype_signing) {
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS:
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP:
padding = LGRSAM_PKCS1_OAEP_PSS;
pad = LGRSAM_PKCS1_OAEP_PSS;
/* fallthru */
case LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5:
if (jwk->kty != LWS_GENCRYPTO_KYT_RSA)
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_RSA)
return -1;
if (lws_genrsa_create(&rsactx, jwk->e, context, padding)) {
if (lws_genrsa_create(&rsactx, jws->jwk->e, jws->context,
pad, LWS_GENHASH_TYPE_UNKNOWN)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
buf = lws_malloc(n, "jws sign");
n = jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len;
buf = lws_malloc(lws_base64_size(n), "jws sign");
if (!buf)
return -1;
n = lws_genrsa_hash_sign(&rsactx, digest, args->hash_type, buf, n);
n = lws_genrsa_hash_sign(&rsactx, digest, jose->alg->hash_type,
buf, n);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_err("%s: lws_genrsa_hash_sign failed\n", __func__);
lws_free(buf);
return -1;
@ -330,12 +648,15 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
lws_free(buf);
if (n < 0) {
lwsl_err("%s: lws_jws_base64_enc failed\n", __func__);
}
return n;
case LWS_JOSE_ENCTYPE_NONE:
return lws_jws_base64_enc((char *)digest,
lws_genhash_size(args->hash_type),
lws_genhash_size(jose->alg->hash_type),
b64_sig, sig_len);
case LWS_JOSE_ENCTYPE_ECDSA:
/* ECDSA using SHA-256/384/512 */
@ -343,47 +664,53 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
/* the key coming in with this makes sense, right? */
/* has to be an EC key :-) */
if (jwk->kty != LWS_GENCRYPTO_KYT_EC)
if (jws->jwk->kty != LWS_GENCRYPTO_KTY_EC)
return -1;
/* key must state its curve */
if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf)
if (!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf)
return -1;
/* must have all his pieces for a private key */
if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_X].buf ||
!jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf ||
!jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf)
if (!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_X].buf ||
!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].buf ||
!jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf)
return -1;
/* key must match the selected alg curve */
if (strcmp((const char *)jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
args->curve_name))
if (strcmp((const char *)
jws->jwk->e[LWS_GENCRYPTO_EC_KEYEL_CRV].buf,
jose->alg->curve_name))
return -1;
if (lws_genecdsa_create(&ecdsactx, context, NULL)) {
if (lws_genecdsa_create(&ecdsactx, jws->context, NULL)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
if (lws_genecdsa_set_key(&ecdsactx, jwk->e)) {
if (lws_genecdsa_set_key(&ecdsactx, jws->jwk->e)) {
lws_genec_destroy(&ecdsactx);
lwsl_notice("%s: ec key import fail\n", __func__);
return -1;
}
m = jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len;
m = lws_gencrypto_bits_to_bytes(jose->alg->keybits_fixed) * 2;
buf = lws_malloc(m, "jws sign");
if (!buf)
return -1;
n = lws_genecdsa_hash_sign(&ecdsactx, digest, args->hash_type,
(uint8_t *)buf, m);
n = lws_genecdsa_hash_sign_jws(&ecdsactx, digest,
jose->alg->hash_type,
jose->alg->keybits_fixed,
(uint8_t *)buf, m);
lws_genec_destroy(&ecdsactx);
if (n < 0) {
lwsl_notice("decrypt fail\n");
lws_free(buf);
lwsl_notice("%s: lws_genecdsa_hash_sign_jws fail\n",
__func__);
return -1;
}
n = lws_jws_base64_enc((char *)buf, m, b64_sig, sig_len);
lws_free(buf);
@ -396,8 +723,57 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
/* unknown key type */
return -1;
hash_fail:
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
/*
* Flattened JWS JSON:
*
* {
* "payload": "<payload contents>",
* "protected": "<integrity-protected header contents>",
* "header": <non-integrity-protected header contents>,
* "signature": "<signature contents>"
* }
*/
LWS_VISIBLE int
lws_jws_write_flattened_json(struct lws_jws *jws, char *flattened, size_t len)
{
size_t n = 0;
if (len < 1)
return 1;
n += lws_snprintf(flattened + n, len - n , "{\"payload\": \"%s\",\n",
jws->map_b64.buf[LJWS_PYLD]);
n += lws_snprintf(flattened + n, len - n , " \"protected\": \"%s\",\n",
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 , " \"signature\": \"%s\"}\n",
jws->map_b64.buf[LJWS_SIG]);
return (n >= len - 1);
}
LWS_VISIBLE int
lws_jws_write_compact(struct lws_jws *jws, char *compact, size_t len)
{
size_t n = 0;
if (len < 1)
return 1;
n += lws_snprintf(compact + n, len - n , "%.*s",
jws->map_b64.len[LJWS_JOSE], jws->map_b64.buf[LJWS_JOSE]);
n += lws_snprintf(compact + n, len - n , ".%.*s",
jws->map_b64.len[LJWS_PYLD], jws->map_b64.buf[LJWS_PYLD]);
n += lws_snprintf(compact + n, len - n , ".%.*s",
jws->map_b64.len[LJWS_SIG], jws->map_b64.buf[LJWS_SIG]);
return n >= len - 1;
}

View file

@ -22,11 +22,3 @@
* to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c.
*/
int
lws_jws_parse_jose(const struct lws_jose_jwe_alg **args,
uint8_t *buf, int n);
int
lws_jwe_parse_jose(const struct lws_jose_jwe_alg **args,
const struct lws_jose_jwe_alg **enc_args,
uint8_t *buf, int n);

View file

@ -21,3 +21,8 @@
void
lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m);
void
lws_jwk_init_jps(struct lejp_ctx *jctx, struct lws_jwk_parse_state *jps,
struct lws_jwk *jwk, lws_jwk_key_import_callback cb,
void *user);

View file

@ -711,10 +711,10 @@ ensure:
if (lseek(ofd, fo, SEEK_SET) < 0)
continue;
lbuf[sizeof(lbuf) - 1] = '\0';
m = read(ofd, lbuf, sizeof(lbuf) - 1);
if (m < 0)
continue;
lbuf[sizeof(lbuf) - 1] = '\0';
p = (unsigned char *)strchr((char *)lbuf, '\n');
if (p)

View file

@ -1,7 +1,7 @@
/*
* Lightweight Embedded JSON Parser
*
* Copyright (C) 2013-2017 Andy Green <andy@warmcat.com>
* Copyright (C) 2013-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
@ -107,7 +107,7 @@ lejp_change_callback(struct lejp_ctx *ctx,
ctx->callback(ctx, LEJPCB_START);
}
static void
void
lejp_check_path_match(struct lejp_ctx *ctx)
{
const char *p, *q;
@ -644,9 +644,11 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->callback(ctx, LEJPCB_COMPLETE);
/* done, return unused amount */
return len;
if (ctx->callback(ctx, LEJPCB_COMPLETE))
goto reject;
else
/* done, return unused amount */
return len;
}
/* pop */
ctx->sp--;

View file

@ -77,19 +77,20 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
/* eg, udp doesn't have to have a peer */
return NULL;
if (af == AF_INET) {
#ifdef LWS_WITH_IPV6
if (af == AF_INET)
#endif
{
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
q = &s->sin_addr;
rlen = sizeof(s->sin_addr);
} else
}
#ifdef LWS_WITH_IPV6
{
else {
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
q = &s->sin6_addr;
rlen = sizeof(s->sin6_addr);
}
#else
return NULL;
#endif
q8 = q;

View file

@ -127,7 +127,14 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
* need to use that in HANDSHAKE2 to understand
* which wsi to actually write on
*/
lws_client_socket_service(wfound, pollfd, wsi);
if (lws_client_socket_service(wfound, pollfd, wsi) < 0) {
/* closed */
lws_vhost_unlock(wsi->vhost);
return -1;
}
lws_callback_on_writable(wsi);
} else
lwsl_debug("%s: didn't find anything in txn q in HS2\n",

View file

@ -1654,6 +1654,7 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
const struct lws_role_ops *role = &role_ops_raw_skt;
const struct lws_protocols *p1, *protocol =
&wsi->vhost->protocols[wsi->vhost->raw_protocol_index];
char ipbuf[64];
int n;
if (wsi->vhost->listen_accept_role &&
@ -1678,9 +1679,14 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
if (wsi->role_ops->adoption_cb[lwsi_role_server(wsi)])
n = wsi->role_ops->adoption_cb[lwsi_role_server(wsi)];
lwsl_notice("%s: vh %s, role %s, protocol %s, cb %d, ah %p\n",
__func__, wsi->vhost->name, role->name, protocol->name, n,
wsi->http.ah);
ipbuf[0] = '\0';
#if !defined(LWS_PLAT_OPTEE)
lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));
#endif
lwsl_notice("%s: vh %s, peer: %s, role %s, "
"protocol %s, cb %d, ah %p\n", __func__, wsi->vhost->name,
ipbuf, role->name, protocol->name, n, wsi->http.ah);
if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0))
return 1;
@ -2630,8 +2636,7 @@ all_sent:
#else
)
#endif
)
{
) {
lwsi_set_state(wsi, LRS_ESTABLISHED);
/* we might be in keepalive, so close it off here */
lws_vfs_file_close(&wsi->http.fop_fd);
@ -2664,7 +2669,12 @@ all_sent:
return 1; /* >0 indicates completed */
}
} while (1); //(!lws_send_pipe_choked(wsi));
/*
* while(1) here causes us to spam the whole file contents into
* a hugely bloated output buffer if it ever can't send the
* whole chunk...
*/
} while (!lws_send_pipe_choked(wsi));
lws_callback_on_writable(wsi);

View file

@ -21,176 +21,460 @@
#include "core/private.h"
/*
* Signing algorithms
*
* These came from RFC7518 (JSON Web Algorithms) Section 3
*
* Cryptographic Algorithms for Digital Signatures and MACs
*/
static const struct lws_jose_jwe_alg lws_gencrypto_jws_alg_map[] = {
/*
* JWSs MAY also be created that do not provide integrity protection.
* Such a JWS is called an Unsecured JWS. An Unsecured JWS uses the
* "alg" value "none" and is formatted identically to other JWSs, but
* MUST use the empty octet sequence as its JWS Signature value.
* Recipients MUST verify that the JWS Signature value is the empty
* octet sequence.
*
* Implementations that support Unsecured JWSs MUST NOT accept such
* objects as valid unless the application specifies that it is
* acceptable for a specific object to not be integrity protected.
* Implementations MUST NOT accept Unsecured JWSs by default. In order
* to mitigate downgrade attacks, applications MUST NOT signal
* acceptance of Unsecured JWSs at a global level, and SHOULD signal
* acceptance on a per-object basis. See Section 8.5 for security
* considerations associated with using this algorithm.
*/
{ /* optional */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
"none", NULL, 0, 0, 0
},
/*
* HMAC with SHA-2 Functions
*
* The HMAC SHA-256 MAC for a JWS is validated by computing an HMAC
* value per RFC 2104, using SHA-256 as the hash algorithm "H", using
* the received JWS Signing Input as the "text" value, and using the
* shared key. This computed HMAC value is then compared to the result
* of base64url decoding the received encoded JWS Signature value. The
* comparison of the computed HMAC value to the JWS Signature value MUST
* be done in a constant-time manner to thwart timing attacks.
*
* Alternatively, the computed HMAC value can be base64url encoded and
* compared to the received encoded JWS Signature value (also in a
* constant-time manner), as this comparison produces the same result as
* comparing the unencoded values. In either case, if the values match,
* the HMAC has been validated.
*/
{ /* required: HMAC using SHA-256 */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA256,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
"none", NULL
"HS256", NULL, 0, 0, 0
},
{ /* required */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA256,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
"HS256", NULL
},
{ /* optional */
{ /* optional: HMAC using SHA-384 */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA384,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
"HS384", NULL
"HS384", NULL, 0, 0, 0
},
{ /* optional */
{ /* optional: HMAC using SHA-512 */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA512,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_NONE,
"HS512", NULL
"HS512", NULL, 0, 0, 0
},
{ /* recommended */
/*
* Digital Signature with RSASSA-PKCS1-v1_5
*
* This section defines the use of the RSASSA-PKCS1-v1_5 digital
* signature algorithm as defined in Section 8.2 of RFC 3447 [RFC3447]
* (commonly known as PKCS #1), using SHA-2 [SHS] hash functions.
*
* A key of size 2048 bits or larger MUST be used with these algorithms.
*
* The RSASSA-PKCS1-v1_5 SHA-256 digital signature is generated as
* follows: generate a digital signature of the JWS Signing Input using
* RSASSA-PKCS1-v1_5-SIGN and the SHA-256 hash function with the desired
* private key. This is the JWS Signature value.
*
* The RSASSA-PKCS1-v1_5 SHA-256 digital signature for a JWS is
* validated as follows: submit the JWS Signing Input, the JWS
* Signature, and the public key corresponding to the private key used
* by the signer to the RSASSA-PKCS1-v1_5-VERIFY algorithm using SHA-256
* as the hash function.
*/
{ /* recommended: RSASSA-PKCS1-v1_5 using SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
"RS256", NULL
"RS256", NULL, 2048, 4096, 0
},
{ /* optional */
{ /* optional: RSASSA-PKCS1-v1_5 using SHA-384 */
LWS_GENHASH_TYPE_SHA384,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
"RS384", NULL
"RS384", NULL, 2048, 4096, 0
},
{ /* optional */
{ /* optional: RSASSA-PKCS1-v1_5 using SHA-512 */
LWS_GENHASH_TYPE_SHA512,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
"RS512", NULL
"RS512", NULL, 2048, 4096, 0
},
{ /* Recommended+ */
/*
* Digital Signature with ECDSA
*
* The ECDSA P-256 SHA-256 digital signature is generated as follows:
*
* 1. Generate a digital signature of the JWS Signing Input using ECDSA
* P-256 SHA-256 with the desired private key. The output will be
* the pair (R, S), where R and S are 256-bit unsigned integers.
* 2. Turn R and S into octet sequences in big-endian order, with each
* array being be 32 octets long. The octet sequence
* representations MUST NOT be shortened to omit any leading zero
* octets contained in the values.
*
* 3. Concatenate the two octet sequences in the order R and then S.
* (Note that many ECDSA implementations will directly produce this
* concatenation as their output.)
*
* 4. The resulting 64-octet sequence is the JWS Signature value.
*
* The ECDSA P-256 SHA-256 digital signature for a JWS is validated as
* follows:
*
* 1. The JWS Signature value MUST be a 64-octet sequence. If it is
* not a 64-octet sequence, the validation has failed.
*
* 2. Split the 64-octet sequence into two 32-octet sequences. The
* first octet sequence represents R and the second S. The values R
* and S are represented as octet sequences using the Integer-to-
* OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1]
* (in big-endian octet order).
* 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to
* the ECDSA P-256 SHA-256 validator.
*/
{ /* Recommended+: ECDSA using P-256 and SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_NONE,
"ES256", "P-256"
"ES256", "P-256", 256, 256, 0
},
{ /* optional */
{ /* optional: ECDSA using P-384 and SHA-384 */
LWS_GENHASH_TYPE_SHA384,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_NONE,
"ES384", "P-384"
"ES384", "P-384", 384, 384, 0
},
{ /* optional */
{ /* optional: ECDSA using P-521 and SHA-512 */
LWS_GENHASH_TYPE_SHA512,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_ECDSA,
LWS_JOSE_ENCTYPE_NONE,
"ES512", "P-521"
"ES512", "P-521", 521, 521, 0
},
#if 0
Not yet supported
{ /* optional */
/*
* Digital Signature with RSASSA-PSS
*
* A key of size 2048 bits or larger MUST be used with this algorithm.
*
* The RSASSA-PSS SHA-256 digital signature is generated as follows:
* generate a digital signature of the JWS Signing Input using RSASSA-
* PSS-SIGN, the SHA-256 hash function, and the MGF1 mask generation
* function with SHA-256 with the desired private key. This is the JWS
* Signature value.
*
* The RSASSA-PSS SHA-256 digital signature for a JWS is validated as
* follows: submit the JWS Signing Input, the JWS Signature, and the
* public key corresponding to the private key used by the signer to the
* RSASSA-PSS-VERIFY algorithm using SHA-256 as the hash function and
* using MGF1 as the mask generation function with SHA-256.
*
*/
{ /* optional: RSASSA-PSS using SHA-256 and MGF1 with SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_NONE,
"PS256", NULL
"PS256", NULL, 2048, 4096, 0
},
{ /* optional */
{ /* optional: RSASSA-PSS using SHA-384 and MGF1 with SHA-384 */
LWS_GENHASH_TYPE_SHA384,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_NONE,
"PS384", NULL
"PS384", NULL, 2048, 4096, 0
},
{ /* optional */
{ /* optional: RSASSA-PSS using SHA-512 and MGF1 with SHA-512*/
LWS_GENHASH_TYPE_SHA512,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_PSS,
LWS_JOSE_ENCTYPE_NONE,
"PS512", NULL
"PS512", NULL, 2048, 4096, 0
},
#endif
};
/*
* These came from RFC7518 (JSON Web Algorithms) Section 4
*
* Cryptographic Algorithms for Key Management
*
* JWE uses cryptographic algorithms to encrypt or determine the Content
* Encryption Key (CEK).
*/
static const struct lws_jose_jwe_alg lws_gencrypto_jwe_alg_map[] = {
{ /* recommended- */
/*
* This section defines the specifics of encrypting a JWE CEK with
* RSAES-PKCS1-v1_5 [RFC3447]. The "alg" (algorithm) Header Parameter
* value "RSA1_5" is used for this algorithm.
*
* A key of size 2048 bits or larger MUST be used with this algorithm.
*/
{ /* recommended-: RSAES-PKCS1-v1_5 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_1_5,
LWS_JOSE_ENCTYPE_NONE,
"RSA1_5", NULL
"RSA1_5", NULL, 2048, 4096, 0
},
{ /* recommended+ */
{ /* recommended+: RSAES OAEP using default parameters */
LWS_GENHASH_TYPE_SHA1,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_NONE,
"RSA-OAEP", NULL
"RSA-OAEP", NULL, 2048, 4096, 0
},
{ /* recommended */
{ /* recommended+: RSAES OAEP using SHA-256 and MGF1 SHA-256 */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_NONE,
"A128KW", NULL
},
{ /* recommended */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_NONE,
"A256KW", NULL
"RSA-OAEP", NULL, 2048, 4096, 0
},
/*
* Key Wrapping with AES Key Wrap
*
* This section defines the specifics of encrypting a JWE CEK with the
* Advanced Encryption Standard (AES) Key Wrap Algorithm [RFC3394] using
* the default initial value specified in Section 2.2.3.1 of that
* document.
*
*
*/
{ /* recommended: AES Key Wrap with AES Key Wrap with defaults
using 128-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_NONE,
"A128KW", NULL, 128, 128, 64
},
{ /* optional: AES Key Wrap with AES Key Wrap with defaults
using 192-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_NONE,
"A192KW", NULL, 192, 192, 64
},
{ /* recommended: AES Key Wrap with AES Key Wrap with defaults
using 256-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_NONE,
"A256KW", NULL, 256, 256, 64
},
/*
* This section defines the specifics of directly performing symmetric
* key encryption without performing a key wrapping step. In this case,
* the shared symmetric key is used directly as the Content Encryption
* Key (CEK) value for the "enc" algorithm. An empty octet sequence is
* used as the JWE Encrypted Key value. The "alg" (algorithm) Header
* Parameter value "dir" is used in this case.
*/
{ /* recommended */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_GCM,
"dir", NULL
LWS_JOSE_ENCTYPE_NONE,
"dir", NULL, 0, 0, 0
},
{ /* recommended+ */
/*
* Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static
* (ECDH-ES)
*
* This section defines the specifics of key agreement with Elliptic
* Curve Diffie-Hellman Ephemeral Static [RFC6090], in combination with
* the Concat KDF, as defined in Section 5.8.1 of [NIST.800-56A]. The
* key agreement result can be used in one of two ways:
*
* 1. directly as the Content Encryption Key (CEK) for the "enc"
* algorithm, in the Direct Key Agreement mode, or
*
* 2. as a symmetric key used to wrap the CEK with the "A128KW",
* "A192KW", or "A256KW" algorithms, in the Key Agreement with Key
* Wrapping mode.
*
* A new ephemeral public key value MUST be generated for each key
* agreement operation.
*
* In Direct Key Agreement mode, the output of the Concat KDF MUST be a
* key of the same length as that used by the "enc" algorithm. In this
* case, the empty octet sequence is used as the JWE Encrypted Key
* value. The "alg" (algorithm) Header Parameter value "ECDH-ES" is
* used in the Direct Key Agreement mode.
*
* In Key Agreement with Key Wrapping mode, the output of the Concat KDF
* MUST be a key of the length needed for the specified key wrapping
* algorithm. In this case, the JWE Encrypted Key is the CEK wrapped
* with the agreed-upon key.
*/
{ /* recommended+: ECDH Ephemeral Static Key agreement Concat KDF */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_NONE,
"ECDH-ES", NULL
"ECDH-ES", NULL, 128, 128, 0
},
{ /* recommended */
{ /* recommended: ECDH-ES + Concat KDF + wrapped by AES128KW */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_NONE,
"ECDH-ES+A128KW", NULL
LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_AES_ECB,
"ECDH-ES+A128KW", NULL, 128, 128, 0
},
{ /* recommended */
{ /* optional: ECDH-ES + Concat KDF + wrapped by AES192KW */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_RSASSA_PKCS1_OAEP,
LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_AES_ECB,
"ECDH-ES+A192KW", NULL, 192, 192, 0
},
{ /* recommended: ECDH-ES + Concat KDF + wrapped by AES256KW */
LWS_GENHASH_TYPE_SHA256,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_ECDHES,
LWS_JOSE_ENCTYPE_AES_ECB,
"ECDH-ES+A256KW", NULL, 256, 256, 0
},
/*
* Key Encryption with AES GCM
*
* This section defines the specifics of encrypting a JWE Content
* Encryption Key (CEK) with Advanced Encryption Standard (AES) in
* Galois/Counter Mode (GCM) ([AES] and [NIST.800-38D]).
*
* Use of an Initialization Vector (IV) of size 96 bits is REQUIRED with
* this algorithm. The IV is represented in base64url-encoded form as
* the "iv" (initialization vector) Header Parameter value.
*
* The Additional Authenticated Data value used is the empty octet
* string.
*
* The requested size of the Authentication Tag output MUST be 128 bits,
* regardless of the key size.
*
* The JWE Encrypted Key value is the ciphertext output.
*
* The Authentication Tag output is represented in base64url-encoded
* form as the "tag" (authentication tag) Header Parameter value.
*
*
* "iv" (Initialization Vector) Header Parameter
*
* The "iv" (initialization vector) Header Parameter value is the
* base64url-encoded representation of the 96-bit IV value used for the
* key encryption operation. This Header Parameter MUST be present and
* MUST be understood and processed by implementations when these
* algorithms are used.
*
* "tag" (Authentication Tag) Header Parameter
*
* The "tag" (authentication tag) Header Parameter value is the
* base64url-encoded representation of the 128-bit Authentication Tag
* value resulting from the key encryption operation. This Header
* Parameter MUST be present and MUST be understood and processed by
* implementations when these algorithms are used.
*/
{ /* optional: Key wrapping with AES GCM using 128-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_NONE,
"ECDH-ES+A256KW", NULL
"A128GCMKW", NULL, 128, 128, 96
},
{ /* optional: Key wrapping with AES GCM using 192-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_NONE,
"A192GCMKW", NULL, 192, 192, 96
},
{ /* optional: Key wrapping with AES GCM using 256-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_AES_ECB,
LWS_JOSE_ENCTYPE_NONE,
"A256GCMKW", NULL, 256, 256, 96
},
/* list terminator */
{ 0, 0, 0, 0, NULL, NULL }
};
/*
* The "enc" (encryption algorithm) Header Parameter identifies the
* content encryption algorithm used to perform authenticated encryption
* on the plaintext to produce the ciphertext and the Authentication
* Tag. This algorithm MUST be an AEAD algorithm with a specified key
* length. The encrypted content is not usable if the "enc" value does
* not represent a supported algorithm. "enc" values should either be
* registered in the IANA "JSON Web Signature and Encryption Algorithms"
* registry established by [JWA] or be a value that contains a
* Collision-Resistant Name. The "enc" value is a case-sensitive ASCII
* string containing a StringOrURI value. This Header Parameter MUST be
* present and MUST be understood and processed by implementations.
*/
static const struct lws_jose_jwe_alg lws_gencrypto_jwe_enc_map[] = {
/*
* AES_128_CBC_HMAC_SHA_256 / 512
*
* It uses the HMAC message authentication code [RFC2104] with the
* SHA-256 hash function [SHS] to provide message authentication, with
* the HMAC output truncated to 128 bits, corresponding to the
@ -213,7 +497,71 @@ static const struct lws_jose_jwe_alg lws_gencrypto_jwe_enc_map[] = {
LWS_GENHMAC_TYPE_SHA256,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_CBC,
"A128CBC-HS256", NULL
"A128CBC-HS256", NULL, 256, 256, 128
},
/*
* AES_192_CBC_HMAC_SHA_384 is based on AES_128_CBC_HMAC_SHA_256, but
* with the following differences:
*
* The input key K is 48 octets long instead of 32.
* ENC_KEY_LEN is 24 octets instead of 16.
* MAC_KEY_LEN is 24 octets instead of 16.
* SHA-384 is used for the HMAC instead of SHA-256.
* The HMAC SHA-384 value is truncated to T_LEN=24 octets instead of 16.
*/
{ /* required */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA384,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_CBC,
"A192CBC-HS384", NULL, 384, 384, 192
},
/*
* AES_256_CBC_HMAC_SHA_512 is based on AES_128_CBC_HMAC_SHA_256, but
* with the following differences:
*
* The input key K is 64 octets long instead of 32.
* ENC_KEY_LEN is 32 octets instead of 16.
* MAC_KEY_LEN is 32 octets instead of 16.
* SHA-512 is used for the HMAC instead of SHA-256.
* The HMAC SHA-512 value is truncated to T_LEN=32 octets instead of 16.
*/
{ /* required */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_SHA512,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_CBC,
"A256CBC-HS512", NULL, 512, 512, 256
},
/*
* The CEK is used as the encryption key.
*
* Use of an IV of size 96 bits is REQUIRED with this algorithm.
*
* The requested size of the Authentication Tag output MUST be 128 bits,
* regardless of the key size.
*/
{ /* recommended: AES GCM using 128-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_GCM,
"A128GCM", NULL, 128, 128, 96
},
{ /* optional: AES GCM using 192-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_GCM,
"A192GCM", NULL, 192, 192, 96
},
{ /* recommended: AES GCM using 256-bit key */
LWS_GENHASH_TYPE_UNKNOWN,
LWS_GENHMAC_TYPE_UNKNOWN,
LWS_JOSE_ENCTYPE_NONE,
LWS_JOSE_ENCTYPE_AES_GCM,
"A256GCM", NULL, 256, 256, 96
},
};
@ -306,3 +654,28 @@ lws_genhmac_size(enum lws_genhmac_types type)
return 0;
}
int
lws_gencrypto_bits_to_bytes(int bits)
{
if (bits & 7)
return (bits / 8) + 1;
return bits / 8;
}
int
lws_base64_size(int bytes)
{
return ((bytes * 4) / 3) + 6;
}
void
lws_gencrypto_destroy_elements(struct lws_gencrypto_keyelem *el, int m)
{
int n;
for (n = 0; n < m; n++)
if (el[n].buf)
lws_free_set_NULL(el[n].buf);
}

View file

@ -57,6 +57,8 @@ lws_genec_dump(struct lws_gencrypto_keyelem *el)
{
int n;
(void)enames;
lwsl_info(" genec %p: crv: '%s'\n", el,
!!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf ?
(char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf: "no curve name");

View file

@ -29,9 +29,9 @@ static int operation_map[] = { MBEDTLS_AES_ENCRYPT, MBEDTLS_AES_DECRYPT };
LWS_VISIBLE int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
int padding, void *engine)
enum enum_aes_padding padding, void *engine)
{
int n;
int n = 0;
ctx->mode = mode;
ctx->k = el;
@ -40,8 +40,12 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
switch (ctx->mode) {
case LWS_GAESM_XTS:
#if defined(MBEDTLS_CIPHER_MODE_XTS)
mbedtls_aes_xts_init(&ctx->u.ctx_xts);
break;
#else
return -1;
#endif
case LWS_GAESM_GCM:
mbedtls_gcm_init(&ctx->u.ctx_gcm);
n = mbedtls_gcm_setkey(&ctx->u.ctx_gcm, MBEDTLS_CIPHER_ID_AES,
@ -107,23 +111,22 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
LWS_VISIBLE int
lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen)
{
int n;
int n = 0;
if (ctx->mode == LWS_GAESM_GCM) {
if (tag) {
n = mbedtls_gcm_finish(&ctx->u.ctx_gcm, tag, tlen);
if (n)
lwsl_notice("%s: mbedtls_gcm_finish: -0x%x\n",
__func__, -n);
else
if (memcmp(ctx->tag, tag, ctx->taglen)) {
lwsl_err("%s: lws_genaes_crypt tag "
"mismatch (bad first)\n",
__func__);
lwsl_hexdump_notice(tag, tlen);
lwsl_hexdump_notice(ctx->tag, ctx->taglen);
n = -1;
}
n = mbedtls_gcm_finish(&ctx->u.ctx_gcm, tag, tlen);
if (n)
lwsl_notice("%s: mbedtls_gcm_finish: -0x%x\n",
__func__, -n);
if (tag && ctx->op == MBEDTLS_AES_DECRYPT && !n) {
if (lws_timingsafe_bcmp(ctx->tag, tag, ctx->taglen)) {
lwsl_err("%s: lws_genaes_crypt tag "
"mismatch (bad first)\n",
__func__);
lwsl_hexdump_notice(tag, tlen);
lwsl_hexdump_notice(ctx->tag, ctx->taglen);
n = -1;
}
}
mbedtls_gcm_free(&ctx->u.ctx_gcm);
return n;
@ -140,15 +143,134 @@ lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen)
return 0;
}
static int
lws_genaes_rfc3394_wrap(int wrap, int cek_bits, const uint8_t *kek,
int kek_bits, const uint8_t *in, uint8_t *out)
{
int n, m, ret = -1, c64 = cek_bits / 64;
mbedtls_aes_context ctx;
uint8_t a[8], b[16];
/*
* notice the KEK key used to perform the wrapping or unwrapping is
* always the size of the AES key used, eg, A128KW == 128 bits. The
* key being wrapped or unwrapped may be larger and is set by the
* 'bits' parameter.
*
* If it's larger than the KEK key size bits, we iterate over it
*/
mbedtls_aes_init(&ctx);
if (wrap) {
/*
* The inputs to the key wrapping process are the KEK and the
* plaintext to be wrapped. The plaintext consists of n 64-bit
* blocks, containing the key data being wrapped.
*
* Inputs: Plaintext, n 64-bit values {P1, P2, ..., Pn},
* and Key, K (the KEK).
* Outputs: Ciphertext, (n+1) 64-bit values
* {C0, C1, ..., Cn}.
*
* The default initial value (IV) is defined to be the
* hexadecimal constant:
*
* A[0] = IV = A6A6A6A6A6A6A6A6
*/
memset(out, 0xa6, 8);
memcpy(out + 8, in, 8 * c64);
n = mbedtls_aes_setkey_enc(&ctx, kek, kek_bits);
} else {
/*
* 2.2.2 Key Unwrap
*
* The inputs to the unwrap process are the KEK and (n+1)
* 64-bit blocks of ciphertext consisting of previously
* wrapped key. It returns n blocks of plaintext consisting
* of the n 64-bit blocks of the decrypted key data.
*
* Inputs: Ciphertext, (n+1) 64-bit values {C0, C1, ..., Cn},
* and Key, K (the KEK).
*
* Outputs: Plaintext, n 64-bit values {P1, P2, ..., Pn}.
*/
memcpy(a, in, 8);
memcpy(out, in + 8, 8 * c64);
n = mbedtls_aes_setkey_dec(&ctx, kek, kek_bits);
}
if (n < 0) {
lwsl_err("%s: setkey failed\n", __func__);
goto bail;
}
if (wrap) {
for (n = 0; n <= 5; n++) {
uint8_t *r = out + 8;
for (m = 1; m <= c64; m++) {
memcpy(b, out, 8);
memcpy(b + 8, r, 8);
if (mbedtls_internal_aes_encrypt(&ctx, b, b))
goto bail;
memcpy(out, b, 8);
out[7] ^= c64 * n + m;
memcpy(r, b + 8, 8);
r += 8;
}
}
ret = 0;
} else {
/*
*
*/
for (n = 5; n >= 0; n--) {
uint8_t *r = out + (c64 - 1) * 8;
for (m = c64; m >= 1; m--) {
memcpy(b, a, 8);
b[7] ^= c64 * n + m;
memcpy(b + 8, r, 8);
if (mbedtls_internal_aes_decrypt(&ctx, b, b))
goto bail;
memcpy(a, b, 8);
memcpy(r, b + 8, 8);
r -= 8;
}
}
ret = 0;
for (n = 0; n < 8; n++)
if (a[n] != 0xa6)
ret = -1;
}
bail:
if (ret)
lwsl_notice("%s: failed\n", __func__);
mbedtls_aes_free(&ctx);
return ret;
}
LWS_VISIBLE int
lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
uint8_t *out, uint8_t *iv_or_nonce_ctr_or_data_unit_16,
uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen)
{
uint8_t iv[16], sb[16];
int n;
uint8_t iv[LWS_JWE_AES_IV_BYTES], sb[16];
int n = 0;
switch (ctx->mode) {
case LWS_GAESM_KW:
/* a key of length ctx->k->len is wrapped by a 128-bit KEK */
n = lws_genaes_rfc3394_wrap(ctx->op == MBEDTLS_AES_ENCRYPT,
ctx->op == MBEDTLS_AES_ENCRYPT ? len * 8 :
(len - 8) * 8, ctx->k->buf,
ctx->k->len * 8,
in, out);
break;
case LWS_GAESM_CBC:
memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16);
n = mbedtls_aes_crypt_cbc(&ctx->u.ctx, ctx->op, len, iv,
@ -181,10 +303,14 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
break;
case LWS_GAESM_OFB:
#if defined(MBEDTLS_CIPHER_MODE_OFB)
memcpy(iv, iv_or_nonce_ctr_or_data_unit_16, 16);
n = mbedtls_aes_crypt_ofb(&ctx->u.ctx, len, nc_or_iv_off, iv,
in, out);
break;
#else
return -1;
#endif
case LWS_GAESM_XTS:
#if defined(MBEDTLS_CIPHER_MODE_XTS)
@ -233,7 +359,7 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx, const uint8_t *in, size_t len,
}
if (n) {
lwsl_notice("%s: enc: -0x%x, len %d\n", __func__, -n, (int)len);
lwsl_notice("%s: failed: -0x%x, len %d\n", __func__, -n, (int)len);
return -1;
}

View file

@ -52,8 +52,10 @@ lws_gencrypto_mbedtls_hash_to_MD_TYPE(enum lws_genhash_types hash_type)
int
lws_gencrypto_mbedtls_rngf(void *context, unsigned char *buf, size_t len)
{
if ((size_t)lws_get_random(context, buf, len) == len)
if ((size_t)lws_get_random(context, buf, len) == len) {
// lwsl_hexdump_err(buf, len);
return 0;
}
lwsl_err("%s: rng failed\n", __func__);
return -1;
}

View file

@ -86,6 +86,8 @@ lws_genec_keypair_import(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
el[LWS_GENCRYPTO_EC_KEYEL_Y].len))
goto bail1;
mbedtls_mpi_lset(&kp.Q.Z, 1);
switch (ctx->genec_alg) {
case LEGENEC_ECDH:
if (mbedtls_ecdh_get_params(ctx->u.ctx_ecdh, &kp, side))
@ -112,6 +114,7 @@ lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
memset(ctx, 0, sizeof(*ctx));
ctx->context = context;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDH;
@ -130,15 +133,16 @@ lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
memset(ctx, 0, sizeof(*ctx));
ctx->context = context;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDSA;
ctx->u.ctx_ecdh = lws_zalloc(sizeof(*ctx->u.ctx_ecdh), "genecdh");
if (!ctx->u.ctx_ecdh)
ctx->u.ctx_ecdsa = lws_zalloc(sizeof(*ctx->u.ctx_ecdsa), "genecdsa");
if (!ctx->u.ctx_ecdsa)
return 1;
mbedtls_ecdh_init(ctx->u.ctx_ecdh);
mbedtls_ecdsa_init(ctx->u.ctx_ecdsa);
return 0;
}
@ -210,7 +214,8 @@ lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
}
mbedtls_ecdsa_init(&ecdsa);
n = mbedtls_ecdsa_genkey(&ecdsa, curve->tls_lib_nid, lws_gencrypto_mbedtls_rngf,
n = mbedtls_ecdsa_genkey(&ecdsa, curve->tls_lib_nid,
lws_gencrypto_mbedtls_rngf,
ctx->context);
if (n) {
lwsl_err("mbedtls_ecdsa_genkey failed 0x%x\n", -n);
@ -241,7 +246,8 @@ lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
goto bail1;
strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name);
for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++) {
for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT;
n++) {
el[n].len = curve->key_bytes;
el[n].buf = lws_malloc(curve->key_bytes, "ec");
if (!el[n].buf)
@ -288,7 +294,7 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
return -22;
}
mbedtls_ecdsa_init(ctx->u.ctx_ecdsa);
//mbedtls_ecdsa_init(ctx->u.ctx_ecdsa);
n = mbedtls_ecdsa_genkey(ctx->u.ctx_ecdsa, curve->tls_lib_nid,
lws_gencrypto_mbedtls_rngf, ctx->context);
if (n) {
@ -301,7 +307,7 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
* lws_gencrypto_keyelems, so they can be serialized, used in jwk etc
*/
kp = (mbedtls_ecp_keypair *)&ctx->u.ctx_ecdsa;
kp = (mbedtls_ecp_keypair *)ctx->u.ctx_ecdsa;
mpi[0] = &kp->Q.X;
mpi[1] = &kp->d;
@ -314,15 +320,18 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
goto bail1;
strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name);
for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++) {
for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT;
n++) {
el[n].len = curve->key_bytes;
el[n].buf = lws_malloc(curve->key_bytes, "ec");
if (!el[n].buf)
goto bail2;
if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf,
curve->key_bytes))
if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf, el[n].len)) {
lwsl_err("%s: mbedtls_mpi_write_binary failed\n", __func__);
goto bail2;
}
}
return 0;
@ -339,65 +348,121 @@ bail1:
}
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
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)
{
mbedtls_md_type_t md_type =
lws_gencrypto_mbedtls_hash_to_MD_TYPE(hash_type);
int n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
size_t hlen = lws_genhash_size(hash_type);
mbedtls_mpi mpi_r, mpi_s;
size_t slen = sig_len;
int n;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
if (md_type < 0)
return -2;
/*
* The ECDSA P-256 SHA-256 digital signature is generated as follows:
*
* 1. Generate a digital signature of the JWS Signing Input using ECDSA
* P-256 SHA-256 with the desired private key. The output will be
* the pair (R, S), where R and S are 256-bit unsigned integers.
*
* 2. Turn R and S into octet sequences in big-endian order, with each
* array being be 32 octets long. The octet sequence
* representations MUST NOT be shortened to omit any leading zero
* octets contained in the values.
*
* 3. Concatenate the two octet sequences in the order R and then S.
* (Note that many ECDSA implementations will directly produce this
* concatenation as their output.)
*
* 4. The resulting 64-octet sequence is the JWS Signature value.
*/
mbedtls_mpi_init(&mpi_r);
mbedtls_mpi_init(&mpi_s);
n = mbedtls_ecdsa_write_signature(ctx->u.ctx_ecdsa, md_type, in,
lws_genhash_size(hash_type), sig,
&slen, lws_gencrypto_mbedtls_rngf,
ctx->context);
n = mbedtls_ecdsa_sign(&ctx->u.ctx_ecdsa->grp, &mpi_r, &mpi_s,
&ctx->u.ctx_ecdsa->d, in, hlen,
lws_gencrypto_mbedtls_rngf, ctx->context);
if (n) {
lwsl_err("%s: mbedtls_ecdsa_write_signature failed: -0x%x\n",
lwsl_err("%s: mbedtls_ecdsa_sign failed: -0x%x\n",
__func__, -n);
goto bail;
goto bail2;
}
if (mbedtls_mpi_write_binary(&mpi_r, sig, keybytes))
goto bail2;
mbedtls_mpi_free(&mpi_r);
if (mbedtls_mpi_write_binary(&mpi_s, sig + keybytes, keybytes))
goto bail1;
mbedtls_mpi_free(&mpi_s);
return (int)slen;
bail:
bail2:
mbedtls_mpi_free(&mpi_r);
bail1:
mbedtls_mpi_free(&mpi_s);
return -3;
}
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sig_verify(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
const uint8_t *sig, size_t sig_len)
lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, int keybits,
const uint8_t *sig, size_t sig_len)
{
mbedtls_md_type_t md_type =
lws_gencrypto_mbedtls_hash_to_MD_TYPE(hash_type);
int n;
int n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
size_t hlen = lws_genhash_size(hash_type);
mbedtls_mpi mpi_r, mpi_s;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
if (md_type < 0)
return -2;
if ((int)sig_len != keybytes * 2)
return -1;
/*
* 1. The JWS Signature value MUST be a 64-octet sequence. If it is
* not a 64-octet sequence, the validation has failed.
*
* 2. Split the 64-octet sequence into two 32-octet sequences. The
* first octet sequence represents R and the second S. The values R
* and S are represented as octet sequences using the Integer-to-
* OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1]
* (in big-endian octet order).
*
* 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to
* the ECDSA P-256 SHA-256 validator.
*/
mbedtls_mpi_init(&mpi_r);
mbedtls_mpi_init(&mpi_s);
if (mbedtls_mpi_read_binary(&mpi_r, sig, keybytes))
return -1;
if (mbedtls_mpi_read_binary(&mpi_s, sig + keybytes, keybytes))
goto bail1;
n = mbedtls_ecdsa_verify(&ctx->u.ctx_ecdsa->grp, in, hlen,
&ctx->u.ctx_ecdsa->Q, &mpi_r, &mpi_s);
mbedtls_mpi_free(&mpi_s);
mbedtls_mpi_free(&mpi_r);
n = mbedtls_ecdsa_read_signature(ctx->u.ctx_ecdsa, in,
lws_genhash_size(hash_type), sig,
sig_len);
if (n) {
lwsl_err("%s: mbedtls_ecdsa_write_signature failed: -0x%x\n",
lwsl_err("%s: mbedtls_ecdsa_verify failed: -0x%x\n",
__func__, -n);
goto bail;
}
return 0;
bail1:
mbedtls_mpi_free(&mpi_r);
bail:
return -3;

View file

@ -62,6 +62,9 @@ lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
int
lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
{
if (!len)
return 0;
switch (ctx->type) {
case LWS_GENHASH_TYPE_SHA1:
MBA(mbedtls_sha1_update)(&ctx->u.sha1, in, len);
@ -147,6 +150,9 @@ lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
int
lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len)
{
if (!len)
return 0;
if (mbedtls_md_hmac_update(&ctx->ctx, in, len))
return -1;

View file

@ -23,6 +23,7 @@
*/
#include "core/private.h"
#include "tls/mbedtls/private.h"
#include <mbedtls/rsa.h>
LWS_VISIBLE void
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el)
@ -38,7 +39,8 @@ static int mode_map[] = { MBEDTLS_RSA_PKCS_V15, MBEDTLS_RSA_PKCS_V21 };
LWS_VISIBLE int
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
struct lws_context *context, enum enum_genrsa_mode mode)
struct lws_context *context, enum enum_genrsa_mode mode,
enum lws_genhash_types oaep_hashid)
{
memset(ctx, 0, sizeof(*ctx));
ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
@ -53,6 +55,9 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
mbedtls_rsa_init(ctx->ctx, mode_map[mode], 0);
ctx->ctx->padding = mode_map[mode];
ctx->ctx->hash_id = lws_gencrypto_mbedtls_hash_to_MD_TYPE(oaep_hashid);
{
int n;
@ -71,6 +76,20 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
return -1;
}
/* mbedtls... compute missing P & Q */
if ( el[LWS_GENCRYPTO_RSA_KEYEL_D].len &&
!el[LWS_GENCRYPTO_RSA_KEYEL_P].len &&
!el[LWS_GENCRYPTO_RSA_KEYEL_Q].len) {
if (mbedtls_rsa_complete(ctx->ctx)) {
lwsl_notice("mbedtls_rsa_complete failed\n");
lws_free_set_NULL(ctx->ctx);
return -1;
}
}
}
ctx->ctx->len = el[LWS_GENCRYPTO_RSA_KEYEL_N].len;
@ -153,15 +172,19 @@ lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
ctx->ctx->len = in_len;
mbedtls_rsa_complete(ctx->ctx);
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, NULL, NULL,
n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, _rngf,
ctx->context,
MBEDTLS_RSA_PUBLIC,
&olen, in, out,
out_max);
break;
case LGRSAM_PKCS1_OAEP_PSS:
n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, NULL, NULL,
n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, _rngf,
ctx->context,
MBEDTLS_RSA_PUBLIC,
NULL, 0,
&olen, in, out, out_max);
@ -178,13 +201,86 @@ lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
return olen;
}
LWS_VISIBLE int
lws_genrsa_private_decrypt(struct lws_genrsa_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;
mbedtls_rsa_complete(ctx->ctx);
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
n = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx->ctx, _rngf,
ctx->context,
MBEDTLS_RSA_PRIVATE,
&olen, in, out,
out_max);
break;
case LGRSAM_PKCS1_OAEP_PSS:
n = mbedtls_rsa_rsaes_oaep_decrypt(ctx->ctx, _rngf,
ctx->context,
MBEDTLS_RSA_PRIVATE,
NULL, 0,
&olen, in, out, out_max);
break;
default:
return -1;
}
if (n) {
lwsl_notice("%s: -0x%x\n", __func__, -n);
return -1;
}
return olen;
}
LWS_VISIBLE int
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out)
{
int n;
ctx->ctx->padding = mode_map[ctx->mode];
mbedtls_rsa_complete(ctx->ctx);
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, _rngf,
ctx->context,
MBEDTLS_RSA_PUBLIC,
in_len, in, out);
break;
case LGRSAM_PKCS1_OAEP_PSS:
n = mbedtls_rsa_rsaes_oaep_encrypt(ctx->ctx, _rngf,
ctx->context,
MBEDTLS_RSA_PUBLIC,
NULL, 0,
in_len, in, out);
break;
default:
return -1;
}
if (n < 0) {
lwsl_notice("%s: -0x%x: in_len: %d\n", __func__, -n,
(int)in_len);
return -1;
}
return mbedtls_mpi_size(&ctx->ctx->N);
}
LWS_VISIBLE int
lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out)
{
int n;
mbedtls_rsa_complete(ctx->ctx);
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
@ -210,7 +306,7 @@ lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
return -1;
}
return 0;
return mbedtls_mpi_size(&ctx->ctx->N);
}
LWS_VISIBLE int
@ -223,6 +319,8 @@ lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
if (h < 0)
return -1;
mbedtls_rsa_complete(ctx->ctx);
switch(ctx->mode) {
case LGRSAM_PKCS1_1_5:
n = mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx->ctx, NULL, NULL,
@ -256,6 +354,8 @@ lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
if (h < 0)
return -1;
mbedtls_rsa_complete(ctx->ctx);
/*
* The "sig" buffer must be as large as the size of ctx->N
* (eg. 128 bytes if RSA-1024 is used).

View file

@ -588,7 +588,7 @@ lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost)
{
}
#if defined(LWS_WITH_JWS)
#if defined(LWS_WITH_JOSE)
static int
_rngf(void *context, unsigned char *buf, size_t len)
{

View file

@ -32,9 +32,9 @@
LWS_VISIBLE int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
int padding, void *engine)
enum enum_aes_padding padding, void *engine)
{
int n;
int n = 0;
ctx->ctx = EVP_CIPHER_CTX_new();
if (!ctx->ctx)
@ -49,6 +49,17 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
switch (ctx->k->len) {
case 128 / 8:
switch (mode) {
case LWS_GAESM_KW:
#if defined(LWS_HAVE_EVP_aes_128_wrap)
EVP_CIPHER_CTX_set_flags(ctx->ctx,
EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
ctx->cipher = EVP_aes_128_wrap();
break;
#else
lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
__func__);
return -1;
#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_128_cbc();
break;
@ -75,12 +86,23 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
ctx->cipher = EVP_aes_128_gcm();
break;
default:
return -1;
goto bail;
}
break;
case 192 / 8:
switch (mode) {
case LWS_GAESM_KW:
#if defined(LWS_HAVE_EVP_aes_128_wrap)
EVP_CIPHER_CTX_set_flags(ctx->ctx,
EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
ctx->cipher = EVP_aes_192_wrap();
break;
#else
lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
__func__);
return -1;
#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_192_cbc();
break;
@ -101,17 +123,28 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
break;
case LWS_GAESM_XTS:
lwsl_err("%s: AES XTS 192 invalid\n", __func__);
return -1;
goto bail;
case LWS_GAESM_GCM:
ctx->cipher = EVP_aes_192_gcm();
break;
default:
return -1;
goto bail;
}
break;
case 256 / 8:
switch (mode) {
case LWS_GAESM_KW:
#if defined(LWS_HAVE_EVP_aes_128_wrap)
EVP_CIPHER_CTX_set_flags(ctx->ctx,
EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
ctx->cipher = EVP_aes_256_wrap();
break;
#else
lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
__func__);
return -1;
#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_256_cbc();
break;
@ -137,7 +170,7 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
ctx->cipher = EVP_aes_256_gcm();
break;
default:
return -1;
goto bail;
}
break;
@ -147,14 +180,14 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
ctx->cipher = EVP_aes_256_xts();
break;
default:
return -1;
goto bail;
}
break;
default:
lwsl_err("%s: unsupported AES size %d bits\n", __func__,
ctx->k->len * 8);
return -1;
goto bail;
}
switch (ctx->op) {
@ -172,11 +205,15 @@ lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
if (!n) {
lwsl_err("%s: cipher init failed (cipher %p)\n", __func__,
ctx->cipher);
return -1;
lws_tls_err_describe();
goto bail;
}
return 0;
bail:
EVP_CIPHER_CTX_free(ctx->ctx);
ctx->ctx = NULL;
return -1;
}
LWS_VISIBLE int
@ -196,31 +233,24 @@ lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen)
lwsl_err("%s: enc final failed\n", __func__);
n = -1;
}
if (ctx->mode == LWS_GAESM_GCM) {
memset(tag, 0, tlen);
if (EVP_CIPHER_CTX_ctrl(ctx->ctx,
EVP_CTRL_GCM_GET_TAG,
ctx->taglen, tag) != 1) {
lwsl_err("get tag ctrl failed\n");
//lws_tls_err_describe();
n = 1;
} else
if (memcmp(tag, ctx->tag, ctx->taglen)) {
lwsl_err("%s: tag mismatch "
"(bad first)\n", __func__);
//lws_tls_err_describe();
lwsl_hexdump_notice(tag, tlen);
lwsl_hexdump_notice(ctx->tag, ctx->taglen);
n = -1;
}
}
break;
case LWS_GAESO_DEC:
if (EVP_DecryptFinal_ex(ctx->ctx, buf, &outl) != 1) {
lwsl_err("%s: dec final failed\n", __func__);
//lws_tls_err_describe();
lws_tls_err_describe();
n = -1;
}
break;
}
if (outl)
@ -240,7 +270,7 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx,
uint8_t *iv_or_nonce_ctr_or_data_unit_16,
uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen)
{
int n, outl, olen;
int n = 0, outl, olen;
if (!ctx->init) {
@ -277,19 +307,20 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx,
return -1;
}
ctx->init = 1;
if (ctx->mode == LWS_GAESM_GCM) {
/* AAD */
if (len)
if (EVP_EncryptUpdate(ctx->ctx, NULL, &olen,
in, len) != 1) {
lwsl_err("%s: set aad failed\n",
__func__);
}
return -1;
}
if (ctx->mode == LWS_GAESM_GCM && !out) {
/* AAD */
if (len)
if (EVP_EncryptUpdate(ctx->ctx, NULL, &olen,
in, len) != 1) {
lwsl_err("%s: set aad failed\n",
__func__);
return 0;
}
return -1;
}
return 0;
}
switch (ctx->op) {
@ -299,12 +330,15 @@ lws_genaes_crypt(struct lws_genaes_ctx *ctx,
case LWS_GAESO_DEC:
n = EVP_DecryptUpdate(ctx->ctx, out, &outl, in, len);
break;
default:
return -1;
}
// lwsl_notice("discarding outl %d\n", (int)outl);
if (!n) {
lwsl_notice("%s: update failed\n", __func__);
lws_tls_err_describe();
return -1;
}

View file

@ -24,6 +24,11 @@
#include "core/private.h"
#include "tls/openssl/private.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int
lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type)
{

View file

@ -24,6 +24,56 @@
#include "core/private.h"
#include "tls/openssl/private.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
#if !defined(LWS_HAVE_ECDSA_SIG_set0)
static void
ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{
if (pr != NULL)
*pr = sig->r;
if (ps != NULL)
*ps = sig->s;
}
static int
ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (r == NULL || s == NULL)
return 0;
BN_clear_free(sig->r);
BN_clear_free(sig->s);
sig->r = r;
sig->s = s;
return 1;
}
#endif
#if !defined(LWS_HAVE_BN_bn2binpad)
static int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
{
int i;
BN_ULONG l;
bn_check_top(a);
i = BN_num_bytes(a);
/* Add leading zeroes if necessary */
if (tolen > i) {
memset(to, 0, tolen - i);
to += tolen - i;
}
while (i--) {
l = a->d[i / BN_BYTES];
*(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff;
}
return tolen;
}
#endif
const struct lws_ec_curves lws_ec_curves[] = {
/*
* These are the curves we are willing to use by default...
@ -88,15 +138,16 @@ lws_genec_eckey_import(int nid, EVP_PKEY *pkey, struct lws_gencrypto_keyelem *el
}
n = EC_KEY_set_private_key(ec, bn_d);
BN_free(bn_d);
BN_clear_free(bn_d);
if (n != 1) {
lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__);
goto bail;
}
if (EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
n = EVP_PKEY_assign_EC_KEY(pkey, ec);
if (n != 1) {
lwsl_err("%s: EVP_PKEY_set1_EC_KEY failed\n", __func__);
goto bail;
return -1;
}
return 0;
@ -141,6 +192,7 @@ lws_genec_keypair_import(const struct lws_ec_curves *curve_table,
*pctx = EVP_PKEY_CTX_new(pkey, NULL);
EVP_PKEY_free(pkey);
pkey = NULL;
if (!*pctx)
goto bail;
@ -210,6 +262,11 @@ lws_genec_keypair_destroy(EVP_PKEY_CTX **pctx)
{
if (!*pctx)
return;
// lwsl_err("%p\n", EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx)));
// EC_KEY_free(EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx)));
EVP_PKEY_CTX_free(*pctx);
*pctx = NULL;
}
@ -234,9 +291,6 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
BIGNUM *bn[3];
EC_KEY *ec;
if (ctx->genec_alg != LEGENEC_ECDH)
return -1;
curve = lws_genec_curve(ctx->curve_table, curve_name);
if (!curve) {
lwsl_err("%s: curve '%s' not supported\n",
@ -246,11 +300,15 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
}
ec = EC_KEY_new_by_curve_name(curve->tls_lib_nid);
if (!ec)
if (!ec) {
lwsl_err("%s: unknown nid %d\n", __func__, curve->tls_lib_nid);
return -23;
}
if (EC_KEY_generate_key(ec) != 1)
if (EC_KEY_generate_key(ec) != 1) {
lwsl_err("%s: EC_KEY_generate_key failed\n", __func__);
goto bail;
}
pkey = EVP_PKEY_new();
if (!pkey)
@ -273,8 +331,10 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
*/
pubkey = EC_KEY_get0_public_key(ec);
if (!pubkey)
if (!pubkey) {
lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__);
goto bail1;
}
bn[0] = BN_new();
bn[1] = (BIGNUM *)EC_KEY_get0_private_key(ec);
@ -290,8 +350,10 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
el[LWS_GENCRYPTO_EC_KEYEL_CRV].len = strlen(curve_name) + 1;
el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf =
lws_malloc(el[LWS_GENCRYPTO_EC_KEYEL_CRV].len, "ec");
if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf)
if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) {
lwsl_err("%s: OOM\n", __func__);
goto bail2;
}
strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name);
@ -302,7 +364,7 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
if (!el[n].buf)
goto bail2;
m = BN_bn2bin(bn[n - 1], el[n].buf);
m = BN_bn2binpad(bn[n - 1], el[n].buf, el[n].len);
if (m != el[n].len)
goto bail2;
}
@ -310,8 +372,8 @@ lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
ret = 0;
bail2:
BN_free(bn[0]);
BN_free(bn[2]);
BN_clear_free(bn[0]);
BN_clear_free(bn[2]);
bail1:
EVP_PKEY_free(pkey);
bail:
@ -341,6 +403,7 @@ lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
return lws_genec_new_keypair(ctx, LDHS_OURS, curve_name, el);
}
#if 0
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
@ -385,47 +448,161 @@ bail:
return -1;
}
#endif
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sig_verify(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
const uint8_t *sig, size_t sig_len)
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)
{
const EVP_MD *md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type);
EVP_MD_CTX *mdctx = NULL;
int ret = -1;
int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
const BIGNUM *r = NULL, *s = NULL;
ECDSA_SIG *ecdsasig;
EC_KEY *eckey;
if (ctx->genec_alg != LEGENEC_ECDSA)
if (ctx->genec_alg != LEGENEC_ECDSA) {
lwsl_notice("%s: ctx alg %d\n", __func__, ctx->genec_alg);
return -1;
}
if (!md)
if ((int)sig_len < keybytes * 2) {
lwsl_notice("%s: sig buff %d < %d\n", __func__,
(int)sig_len, keybytes * 2);
return -1;
}
mdctx = EVP_MD_CTX_create();
if (!mdctx)
goto bail;
eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx));
if (EVP_DigestVerifyInit(mdctx, NULL, md, NULL,
EVP_PKEY_CTX_get0_pkey(ctx->ctx))) {
lwsl_err("%s: EVP_DigestSignInit failed\n", __func__);
/*
* The ECDSA P-256 SHA-256 digital signature is generated as follows:
*
* 1. Generate a digital signature of the JWS Signing Input using ECDSA
* P-256 SHA-256 with the desired private key. The output will be
* the pair (R, S), where R and S are 256-bit unsigned integers.
*
* 2. Turn R and S into octet sequences in big-endian order, with each
* array being be 32 octets long. The octet sequence
* representations MUST NOT be shortened to omit any leading zero
* octets contained in the values.
*
* 3. Concatenate the two octet sequences in the order R and then S.
* (Note that many ECDSA implementations will directly produce this
* concatenation as their output.)
*
* 4. The resulting 64-octet sequence is the JWS Signature value.
*/
ecdsasig = ECDSA_do_sign(in, lws_genhash_size(hash_type), eckey);
EC_KEY_free(eckey);
if (!ecdsasig) {
lwsl_notice("%s: ECDSA_do_sign fail\n", __func__);
goto bail;
}
if (EVP_DigestVerifyUpdate(mdctx, in, EVP_MD_size(md))) {
lwsl_err("%s: EVP_DigestSignUpdate failed\n", __func__);
ECDSA_SIG_get0(ecdsasig, &r, &s);
/*
* in the 521-bit case, we have to pad the last byte as it only
* generates 65 bytes
*/
n = BN_bn2binpad(r, sig, keybytes);
if (n != keybytes) {
lwsl_notice("%s: bignum r fail %d %d\n", __func__, n, keybytes);
goto bail;
}
if (EVP_DigestVerifyFinal(mdctx, sig, sig_len)) {
lwsl_err("%s: EVP_DigestSignFinal failed\n", __func__);
n = BN_bn2binpad(s, sig + keybytes, keybytes);
if (n != keybytes) {
lwsl_notice("%s: bignum s fail %d %d\n", __func__, n, keybytes);
goto bail;
}
ret = 0;
bail:
if (mdctx)
EVP_MD_CTX_free(mdctx);
if (ecdsasig)
ECDSA_SIG_free(ecdsasig);
return ret;
}
/* in is the JWS Signing Input hash */
LWS_VISIBLE LWS_EXTERN int
lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, int keybits,
const uint8_t *sig, size_t sig_len)
{
int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits),
hlen = lws_genhash_size(hash_type);
ECDSA_SIG *ecsig = ECDSA_SIG_new();
BIGNUM *r = NULL, *s = NULL;
EC_KEY *eckey;
if (!ecsig)
return -1;
if (ctx->genec_alg != LEGENEC_ECDSA)
goto bail;
if ((int)sig_len != keybytes * 2) {
lwsl_err("%s: sig buf too small %d vs %d\n", __func__,
(int)sig_len, keybytes * 2);
goto bail;
}
/*
* 1. The JWS Signature value MUST be a 64-octet sequence. If it is
* not a 64-octet sequence, the validation has failed.
*
* 2. Split the 64-octet sequence into two 32-octet sequences. The
* first octet sequence represents R and the second S. The values R
* and S are represented as octet sequences using the Integer-to-
* OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1]
* (in big-endian octet order).
*
* 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to
* the ECDSA P-256 SHA-256 validator.
*/
r = BN_bin2bn(sig, keybytes, NULL);
if (!r) {
lwsl_err("%s: BN_bin2bn (r) fail\n", __func__);
goto bail;
}
s = BN_bin2bn(sig + keybytes, keybytes, NULL);
if (!s) {
lwsl_err("%s: BN_bin2bn (s) fail\n", __func__);
goto bail1;
}
if (ECDSA_SIG_set0(ecsig, r, s) != 1) {
lwsl_err("%s: ECDSA_SIG_set0 fail\n", __func__);
goto bail1;
}
eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx));
n = ECDSA_do_verify(in, hlen, ecsig, eckey);
EC_KEY_free(eckey);
if (n != 1) {
lwsl_err("%s: ECDSA_do_verify fail\n", __func__);
lws_tls_err_describe();
goto bail;
}
ret = 0;
goto bail;
bail1:
if (r)
BN_free(r);
if (s)
BN_free(s);
bail:
ECDSA_SIG_free(ecsig);
return ret;
}

View file

@ -23,6 +23,11 @@
*/
#include "libwebsockets.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int
lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
{
@ -60,6 +65,9 @@ lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
int
lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
{
if (!len)
return 0;
return EVP_DigestUpdate(ctx->mdctx, in, len) != 1;
}

View file

@ -24,14 +24,15 @@
#include "core/private.h"
#include "tls/openssl/private.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
LWS_VISIBLE void
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el)
{
int n;
for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++)
if (el[n].buf)
lws_free_set_NULL(el[n].buf);
lws_gencrypto_destroy_elements(el, LWS_GENCRYPTO_RSA_KEYEL_COUNT);
}
static int mode_map_crypt[] = { RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING },
@ -74,7 +75,8 @@ bail:
LWS_VISIBLE int
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
struct lws_context *context, enum enum_genrsa_mode mode)
struct lws_context *context, enum enum_genrsa_mode mode,
enum lws_genhash_types oaep_hashid)
{
int n;
@ -129,7 +131,7 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
bail:
for (n = 0; n < 5; n++)
if (ctx->bn[n]) {
BN_free(ctx->bn[n]);
BN_clear_free(ctx->bn[n]);
ctx->bn[n] = NULL;
}
@ -168,7 +170,7 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
}
n = RSA_generate_key_ex(ctx->rsa, bits, bn, NULL);
BN_free(bn);
BN_clear_free(bn);
if (n != 1)
goto cleanup_1;
@ -219,27 +221,59 @@ LWS_VISIBLE int
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out)
{
if (RSA_public_encrypt((int)in_len, in, out, ctx->rsa,
mode_map_crypt[ctx->mode]) < 0) {
int n = RSA_public_encrypt((int)in_len, in, out, ctx->rsa,
mode_map_crypt[ctx->mode]);
if (n < 0) {
lwsl_err("%s: RSA_public_encrypt failed\n", __func__);
lws_tls_err_describe();
return -1;
}
return 0;
return n;
}
LWS_VISIBLE int
lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out)
{
int n = RSA_private_encrypt((int)in_len, in, out, ctx->rsa,
mode_map_crypt[ctx->mode]);
if (n < 0) {
lwsl_err("%s: RSA_private_encrypt failed\n", __func__);
lws_tls_err_describe();
return -1;
}
return n;
}
LWS_VISIBLE int
lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max)
{
if (RSA_public_decrypt((int)in_len, in, out, ctx->rsa,
mode_map_crypt[ctx->mode]) < 0) {
int n = RSA_public_decrypt((int)in_len, in, out, ctx->rsa,
mode_map_crypt[ctx->mode]);
if (n < 0) {
lwsl_err("%s: RSA_public_decrypt failed\n", __func__);
return -1;
}
return 0;
return n;
}
LWS_VISIBLE int
lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
size_t in_len, uint8_t *out, size_t out_max)
{
int n = RSA_private_decrypt((int)in_len, in, out, ctx->rsa,
mode_map_crypt[ctx->mode]);
if (n < 0) {
lwsl_err("%s: RSA_private_decrypt failed\n", __func__);
lws_tls_err_describe();
return -1;
}
return n;
}
LWS_VISIBLE int
@ -271,7 +305,8 @@ lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
}
if (n != 1) {
lwsl_notice("%s: -0x%x\n", __func__, -n);
lwsl_notice("%s: fail\n", __func__);
lws_tls_err_describe();
return -1;
}

View file

@ -21,6 +21,11 @@
#include "core/private.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int lws_openssl_describe_cipher(struct lws *wsi);
extern int openssl_websocket_private_data_index,

View file

@ -21,6 +21,11 @@
#include "core/private.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;

View file

@ -25,6 +25,11 @@
int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int lws_openssl_describe_cipher(struct lws *wsi)
{
#if !defined(LWS_WITH_NO_LOGS)

View file

@ -293,6 +293,11 @@ lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
int
lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt);
int
lws_gencrypto_bits_to_bytes(int bits);
void
lws_gencrypto_destroy_elements(struct lws_gencrypto_keyelem *el, int m);
/* genec */

View file

@ -1,6 +1,7 @@
|name|demonstrates|
---|---
client-server|Minimal examples providing client and server connections simultaneously
crypto|Minimal examples related to using lws crypto apis
dbus-server|Minimal examples showing how to integrate DBUS into lws event loop
http-client|Minimal examples providing an http client
http-server|Minimal examples providing an http server

View file

@ -1,5 +1,10 @@
These are buildable test apps that run in CI to confirm correct api operation.
|name|tests|
---|---
api-test-lwsac|LWS Allocated Chunks
api-test-lws_tokenize|Generic secure string tokenizer
api-test-lwsac|LWS Allocated Chunks api
api-test-lws_tokenize|Generic secure string tokenizer api
api-test-fts|LWS Full-text Search api
api-test-gencrypto|LWS Generic Crypto apis
api-test-jose|LWS JOSE apis

View file

@ -64,7 +64,7 @@ test_genaes_cbc(void)
return -1;
}
if (memcmp(cbc256_enc, res, 16)) {
if (lws_timingsafe_bcmp(cbc256_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -88,7 +88,7 @@ test_genaes_cbc(void)
return -1;
}
if (memcmp(cbc256, res1, 16)) {
if (lws_timingsafe_bcmp(cbc256, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -151,7 +151,7 @@ test_genaes_cfb128(void)
return -1;
}
if (memcmp(cfb128_enc, res, 16)) {
if (lws_timingsafe_bcmp(cfb128_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -175,7 +175,7 @@ test_genaes_cfb128(void)
return -1;
}
if (memcmp(cfb128, res1, 16)) {
if (lws_timingsafe_bcmp(cfb128, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res1, 16);
return -1;
@ -237,7 +237,7 @@ test_genaes_cfb8(void)
return -1;
}
if (memcmp(cfb8_enc, res, 16)) {
if (lws_timingsafe_bcmp(cfb8_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -259,7 +259,7 @@ test_genaes_cfb8(void)
return -1;
}
if (memcmp(cfb8, res1, 16)) {
if (lws_timingsafe_bcmp(cfb8, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res1, 16);
return -1;
@ -325,7 +325,7 @@ test_genaes_ctr(void)
return -1;
}
if (memcmp(ctr_enc, res, 16)) {
if (lws_timingsafe_bcmp(ctr_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -350,7 +350,7 @@ test_genaes_ctr(void)
return -1;
}
if (memcmp(ctr, res1, 16)) {
if (lws_timingsafe_bcmp(ctr, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res1, 16);
return -1;
@ -415,7 +415,7 @@ test_genaes_ecb(void)
return -1;
}
if (memcmp(ecb_enc, res, 16)) {
if (lws_timingsafe_bcmp(ecb_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -436,7 +436,7 @@ test_genaes_ecb(void)
return -1;
}
if (memcmp(ecb, res1, 16)) {
if (lws_timingsafe_bcmp(ecb, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -450,6 +450,9 @@ bail:
return -1;
}
#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_OFB)
#else
static const uint8_t
/*
* produced with (plaintext.txt contains "test plaintext\0\0")
@ -506,7 +509,7 @@ test_genaes_ofb(void)
return -1;
}
if (memcmp(ofb_enc, res, 16)) {
if (lws_timingsafe_bcmp(ofb_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -530,7 +533,7 @@ test_genaes_ofb(void)
return -1;
}
if (memcmp(ofb, res1, 16)) {
if (lws_timingsafe_bcmp(ofb, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -544,6 +547,8 @@ bail:
return -1;
}
#endif
static const uint8_t
/*
* Fedora openssl tool doesn't support xts... this data produced
@ -594,7 +599,7 @@ test_genaes_xts(void)
return -1;
}
if (memcmp(xts_enc, res, 16)) {
if (lws_timingsafe_bcmp(xts_enc, res, 16)) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -615,7 +620,7 @@ test_genaes_xts(void)
return -1;
}
if (memcmp(xts, res1, 16)) {
if (lws_timingsafe_bcmp(xts, res1, 16)) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
return -1;
@ -697,7 +702,7 @@ test_genaes_gcm(void)
return -1;
}
if (memcmp(gcm_ct, res, sizeof(gcm_ct))) {
if (lws_timingsafe_bcmp(gcm_ct, res, sizeof(gcm_ct))) {
lwsl_err("%s: lws_genaes_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, sizeof(gcm_ct));
return -1;
@ -730,7 +735,7 @@ test_genaes_gcm(void)
return -1;
}
if (memcmp(gcm_pt, res, sizeof(gcm_pt))) {
if (lws_timingsafe_bcmp(gcm_pt, res, sizeof(gcm_pt))) {
lwsl_err("%s: lws_genaes_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, sizeof(gcm_ct));
return -1;
@ -763,8 +768,11 @@ test_genaes(struct lws_context *context)
if (test_genaes_ecb())
goto bail;
#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_OFB)
#else
if (test_genaes_ofb())
goto bail;
#endif
#if defined(MBEDTLS_CONFIG_H) && !defined(MBEDTLS_CIPHER_MODE_XTS)
#else

View file

@ -54,7 +54,7 @@ test_genec1(struct lws_context *context)
lws_jwk_dump(&jwk);
if (jwk.kty != LWS_GENCRYPTO_KYT_EC) {
if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
lws_jwk_destroy(&jwk);
lwsl_err("%s: jwk is not an EC key\n", __func__);
return 1;
@ -76,7 +76,7 @@ test_genec1(struct lws_context *context)
goto bail;
}
if (memcmp(cbc256_enc, res, 16)) {
if (lws_timingsafe_bcmp(cbc256_enc, res, 16)) {
lwsl_err("%s: lws_genec_crypt encoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
goto bail;
@ -95,7 +95,7 @@ test_genec1(struct lws_context *context)
goto bail;
}
if (memcmp(cbc256, res1, 16)) {
if (lws_timingsafe_bcmp(cbc256, res1, 16)) {
lwsl_err("%s: lws_genec_crypt decoding mismatch\n", __func__);
lwsl_hexdump_notice(res, 16);
goto bail;

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,83 @@
*
*/
/* for none, the compact serialization format is b64u(jose hdr).b64u(payload) */
static const char *none_cser =
"eyJhbGciOiJub25lIn0"
"."
"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
*none_jose = "{\"alg\":\"none\"}",
*none_payload = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
" \"http://example.com/is_root\":true}";
int
test_jws_none(struct lws_context *context)
{
struct lws_jws_compact_map map;
struct lws_jose jose;
char temp[2048];
int n, temp_len = sizeof(temp);
lws_jose_init(&jose);
/* A.5 Unsecured JSON "none" RFC7515 worked example */
/* decode the b64.b64[.b64] compact serialization blocks */
n = lws_jws_compact_decode(none_cser, strlen(none_cser), &map, NULL,
temp, &temp_len);
if (n != 2) {
lwsl_err("%s: concat_map failed\n", __func__);
goto bail;
}
/* confirm the decoded JOSE header is exactly what we expect */
if (strncmp(none_jose, map.buf[LJWS_JOSE], map.len[LJWS_JOSE])) {
lwsl_err("%s: jose b64 decode wrong\n", __func__);
goto bail;
}
/* parse the JOSE header */
if (lws_jws_parse_jose(&jose, map.buf[LJWS_JOSE],
map.len[LJWS_JOSE],
(char *)lws_concat_temp(temp, temp_len),
&temp_len) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
goto bail;
}
/* confirm we used the "none" alg as expected from JOSE hdr */
if (strcmp(jose.alg->alg, "none")) {
lwsl_err("%s: JOSE header has wrong alg\n", __func__);
goto bail;
}
/* confirm the payload is literally what we expect */
if (strncmp(none_payload, map.buf[LJWS_PYLD],
map.len[LJWS_PYLD])) {
lwsl_err("%s: payload b64 decode wrong\n", __func__);
goto bail;
}
/* end */
lwsl_notice("%s: selftest OK\n", __func__);
lws_jose_destroy(&jose);
return 0;
bail:
lws_jose_destroy(&jose);
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
return 1;
}
static const char
*test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
*test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
@ -32,9 +109,127 @@ static const char
*key_jwk = "{\"kty\":\"oct\",\r\n"
" \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ"
"Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}",
*hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
/* the key from worked example in RFC7515 A-1, as a JWK */
*rfc7515_rsa_key =
*hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
;
int
test_jws_HS256(struct lws_context *context)
{
char buf[2048], temp[256], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr;
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_jws_compact_map map;
int temp_len = sizeof(temp);
struct lws_genhmac_ctx ctx;
struct lws_jose jose;
struct lws_jwk jwk;
struct lws_jws jws;
int n;
lws_jose_init(&jose);
lws_jws_init(&jws, &jwk, context);
/* Test 1: SHA256 on RFC7515 worked example */
/* parse the JOSE header */
if (lws_jws_parse_jose(&jose, test1, strlen(test1), temp, &temp_len) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
goto bail;
}
/* confirm we used the "none" alg as expected from JOSE hdr */
if (strcmp(jose.alg->alg, "HS256")) {
lwsl_err("%s: JOSE header has wrong alg\n", __func__);
goto bail;
}
/* 1.1: import the JWK oct key */
if (lws_jwk_import(&jwk, NULL, NULL, key_jwk, strlen(key_jwk)) < 0) {
lwsl_notice("Failed to decode JWK test key\n");
return -1;
}
if (jwk.kty != LWS_GENCRYPTO_KTY_OCT) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
return -1;
}
/* 1.2: create JWS known hdr + known payload */
n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
if (n < 0) {
goto bail;
}
if (strcmp(buf, test1_enc))
goto bail;
enc_ptr = p + 1; /* + 1 skips the . */
n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
if (n < 0) {
goto bail;
}
if (strcmp(enc_ptr, test2_enc))
goto bail;
/* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
if (lws_genhmac_init(&ctx, jose.alg->hmac_type,
jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf,
jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].len))
goto bail;
if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
goto bail_destroy_hmac;
lws_genhmac_destroy(&ctx, digest);
/* 1.4: append a base64 encode of the computed HMAC digest */
enc_ptr = p + 1; /* + 1 skips the . */
n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
if (n < 0)
goto bail;
if (strcmp(enc_ptr, hash_enc)) { /* check against known B64URL hash */
lwsl_err("%s: b64 enc of computed HMAC mismatches '%s' '%s'\n",
__func__, enc_ptr, hash_enc);
goto bail;
}
/* 1.5: Check we can agree the signature matches the payload */
if (lws_jws_sig_confirm_compact_b64(buf, p - buf, &map, &jwk, context,
lws_concat_temp(temp, temp_len), &temp_len) < 0) {
lwsl_notice("%s: confirm sig failed\n", __func__);
goto bail;
}
lws_jws_destroy(&jws);
lws_jwk_destroy(&jwk);
lws_jose_destroy(&jose);
/* end */
lwsl_notice("%s: selftest OK\n", __func__);
return 0;
bail_destroy_hmac:
lws_genhmac_destroy(&ctx, NULL);
bail:
lws_jws_destroy(&jws);
lws_jwk_destroy(&jwk);
lws_jose_destroy(&jose);
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
return 1;
}
static const char
/* the key from worked example in RFC7515 A-2, as a JWK */
*rfc7515_rsa_key =
"{\"kty\":\"RSA\","
" \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
@ -65,37 +260,137 @@ static const char
"y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
"W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\""
"}",
*rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
"eyJhbGciOiJSUzI1NiJ9"
".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
"p0igcN_IoypGlUPQGe77Rw"
*rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
"eyJhbGciOiJSUzI1NiJ9"
".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
"p0igcN_IoypGlUPQGe77Rw"
;
#if 0
,
*rfc7515_ec_a3_jose = "{\"alg\":\"ES256\"}",
/* payload is the same as test2 above */
*rfc7515_ec_a3_b64_serialization =
"eyJhbGciOiJFUzI1NiJ9"
"."
"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
"."
"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSA"
"pmWQxfKTUJqPP3-Kg6NU1Q",
*rfc7515_ec_a3_jwk =
"{"
int
test_jws_RS256(struct lws_context *context)
{
struct lws_jws_compact_map map;
struct lws_jose jose;
struct lws_jwk jwk;
struct lws_jws jws;
char temp[2048], *in;
int n, l, temp_len = sizeof(temp);
lws_jose_init(&jose);
lws_jws_init(&jws, &jwk, context);
/* Test 2: RS256 on RFC7515 worked example */
if (lws_gencrypto_jws_alg_to_definition("RS256", &jose.alg)) {
lwsl_err("%s: RS256 not supported\n", __func__);
goto bail;
}
/* 2.1: import the jwk */
if (lws_jwk_import(&jwk, NULL, NULL,
rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
lwsl_notice("%s: 2.2: Failed to read JWK key\n", __func__);
goto bail2;
}
if (jwk.kty != LWS_GENCRYPTO_KTY_RSA) {
lwsl_err("%s: 2.2: kty: %d instead of RSA\n", __func__, jwk.kty);
goto bail;
}
/* 2.2: check the signature on the test packet from RFC7515 A-1 */
if (lws_jws_sig_confirm_compact_b64(rfc7515_rsa_a1,
strlen(rfc7515_rsa_a1), &map,
&jwk, context, temp, &temp_len) < 0) {
lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
goto bail;
}
if (lws_jws_b64_compact_map(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
&jws.map_b64) != 3) {
lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__);
goto bail;
}
/* 2.3: generate our own signature for a copy of the test packet */
in = lws_concat_temp(temp, temp_len);
l = strlen(rfc7515_rsa_a1);
if (temp_len < l + 1)
goto bail;
memcpy(in, rfc7515_rsa_a1, l + 1);
temp_len -= l + 1;
if (lws_jws_b64_compact_map(in, l, &jws.map_b64) != 3) {
lwsl_notice("%s: lws_jws_b64_compact_map failed\n", __func__);
goto bail;
}
/* overwrite the copy of the known b64 sig (it's all placed inside temp) */
n = lws_jws_sign_from_b64(&jose, &jws,
(char *)jws.map_b64.buf[LJWS_SIG],
jws.map_b64.len[LJWS_SIG] + 8);
if (n < 0) {
lwsl_err("%s: failed signing test packet\n", __func__);
goto bail;
}
jws.map_b64.len[LJWS_SIG] = n;
/* 2.4: confirm our signature can be verified */
in[l] = '\0';
if (lws_jws_sig_confirm_compact_b64(in, l, &map, &jwk, context, lws_concat_temp(temp, temp_len), &temp_len) < 0) {
lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
goto bail;
}
lws_jwk_destroy(&jwk);
/* end */
lwsl_notice("%s: selftest OK\n", __func__);
return 0;
bail:
lws_jwk_destroy(&jwk);
bail2:
lws_jws_destroy(&jws);
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
return 1;
}
static const char
*es256_jose = "{\"alg\":\"ES256\"}",
*es256_payload = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
" \"http://example.com/is_root\":true}",
*es256_cser =
"eyJhbGciOiJFUzI1NiJ9"
"."
"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
"."
"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSA"
"pmWQxfKTUJqPP3-Kg6NU1Q",
*es256_jwk =
"{"
"\"kty\":\"EC\","
"\"crv\":\"P-256\","
"\"x\":\"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU\","
"\"y\":\"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0\","
"\"d\":\"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI\""
"}",
"}"
#if 0
,
rfc7515_ec_a3_R[] = {
14, 209, 33, 83, 121, 99, 108, 72, 60, 47, 127, 21, 88,
7, 212, 2, 163, 178, 40, 3, 58, 249, 124, 126, 23, 129,
@ -109,152 +404,309 @@ static const char
#endif
;
/*
* These are the inputs and outputs from the worked example in RFC7515
* Appendix A.1.
*
* 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
* a fixed BASE64URL result.
*
* 2) has a fixed header + payload and is signed with a key given in JWK format
*/
int
test_jws_ES256(struct lws_context *context)
{
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_genhash_ctx hash_ctx;
struct lws_jws_compact_map map;
struct lws_jose jose;
struct lws_jwk jwk;
struct lws_jws jws;
char temp[2048], *p;
int ret = -1, l, n, temp_len = sizeof(temp);
/* A.3 "ES256" RFC7515 worked example - verify */
lws_jose_init(&jose);
/* decode the b64.b64[.b64] compact serialization blocks */
if (lws_jws_compact_decode(es256_cser, strlen(es256_cser),
&jws.map, &jws.map_b64,
temp, &temp_len) != 3) {
lwsl_err("%s: concat_map failed\n", __func__);
goto bail;
}
/* confirm the decoded JOSE header is exactly what we expect */
if (jws.map.len[LJWS_JOSE] != strlen(es256_jose) ||
strncmp(es256_jose, jws.map.buf[LJWS_JOSE],
jws.map.len[LJWS_JOSE])) {
lwsl_err("%s: jose b64 decode wrong\n", __func__);
goto bail;
}
/* confirm the decoded payload is exactly what we expect */
if (jws.map.len[LJWS_PYLD] != strlen(es256_payload) ||
strncmp(es256_payload, jws.map.buf[LJWS_PYLD],
jws.map.len[LJWS_PYLD])) {
lwsl_err("%s: payload b64 decode wrong\n", __func__);
goto bail;
}
/* parse the JOSE header */
if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE],
jws.map.len[LJWS_JOSE],
(char *)lws_concat_temp(temp, temp_len), &temp_len) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
goto bail;
}
/* confirm we used "ES256" alg we expect from the JOSE hdr */
if (strcmp(jose.alg->alg, "ES256")) {
lwsl_err("%s: JOSE header has wrong alg\n", __func__);
goto bail;
}
jws.jwk = &jwk;
jws.context = context;
/* import the ES256 jwk */
if (lws_jwk_import(&jwk, NULL, NULL, es256_jwk, strlen(es256_jwk))) {
lwsl_notice("%s: Failed to read JWK key\n", __func__);
goto bail;
}
/* sanity */
if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
lwsl_err("%s: kty: %d instead of EC\n",
__func__, jwk.kty);
goto bail1;
}
if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) {
lwsl_notice("%s: confirm EC sig failed\n", __func__);
goto bail1;
}
/* A.3 "ES256" RFC7515 worked example - sign */
l = strlen(es256_cser);
if (temp_len < l)
goto bail1;
p = lws_concat_temp(temp, temp_len);
memcpy(p, es256_cser, l + 1);
temp_len -= l + 1;
/* scan the b64 compact serialization string to map the blocks */
if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3)
goto bail1;
/* create the hash of the protected b64 part */
if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE],
jws.map_b64.len[LJWS_JOSE]) ||
lws_genhash_update(&hash_ctx, ".", 1) ||
lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD],
jws.map_b64.len[LJWS_PYLD]) ||
lws_genhash_destroy(&hash_ctx, digest)) {
lws_genhash_destroy(&hash_ctx, NULL);
goto bail1;
}
lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]);
/* overwrite the copy of the known b64 sig (it's placed inside buf) */
n = lws_jws_sign_from_b64(&jose, &jws,
(char *)jws.map_b64.buf[LJWS_SIG],
jws.map_b64.len[LJWS_SIG] + 8);
if (n < 0) {
lwsl_err("%s: failed signing test packet\n", __func__);
goto bail1;
}
jws.map_b64.len[LJWS_SIG] = n;
lwsl_hexdump(jws.map_b64.buf[LJWS_SIG], jws.map_b64.len[LJWS_SIG]);
/* 2.4: confirm our generated signature can be verified */
p[l] = '\0';
if (lws_jws_sig_confirm_compact_b64(p, l, &map, &jwk, context, lws_concat_temp(temp, temp_len), &temp_len) < 0) {
lwsl_notice("%s: confirm our EC sig failed\n", __func__);
goto bail1;
}
/* end */
ret = 0;
bail1:
lws_jwk_destroy(&jwk);
lws_jose_destroy(&jose);
bail:
lwsl_notice("%s: selftest %s\n", __func__, ret < 0 ? "FAIL" : "OK");
return ret;
}
static const char
*es512_jose = "{\"alg\":\"ES512\"}",
*es512_payload = "Payload",
*es512_cser =
"eyJhbGciOiJFUzUxMiJ9"
"."
"UGF5bG9hZA"
"."
"AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZq"
"wqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8Kp"
"EHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn",
*es512_jwk =
"{"
"\"kty\":\"EC\","
"\"crv\":\"P-521\","
"\"x\":\"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_"
"NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk\","
"\"y\":\"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDl"
"y79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2\","
"\"d\":\"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPA"
"xerEzgdRhajnu0ferB0d53vM9mE15j2C\""
"}"
;
int
test_jws_ES512(struct lws_context *context)
{
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_genhash_ctx hash_ctx;
struct lws_jws_compact_map map;
struct lws_jose jose;
struct lws_jwk jwk;
struct lws_jws jws;
char temp[2048], *p;
int ret = -1, l, n, temp_len = sizeof(temp);
/* A.4 "ES512" RFC7515 worked example - verify */
lws_jose_init(&jose);
/* decode the b64.b64[.b64] compact serialization blocks */
if (lws_jws_compact_decode(es512_cser, strlen(es512_cser),
&jws.map, &jws.map_b64, temp,
&temp_len) != 3) {
lwsl_err("%s: concat_map failed\n", __func__);
goto bail;
}
/* confirm the decoded JOSE header is exactly what we expect */
if (jws.map.len[LJWS_JOSE] != strlen(es512_jose) ||
strncmp(es512_jose, jws.map.buf[LJWS_JOSE],
jws.map.len[LJWS_JOSE])) {
lwsl_err("%s: jose b64 decode wrong\n", __func__);
goto bail;
}
/* confirm the decoded payload is exactly what we expect */
if (jws.map.len[LJWS_PYLD] != strlen(es512_payload) ||
strncmp(es512_payload, jws.map.buf[LJWS_PYLD],
jws.map.len[LJWS_PYLD])) {
lwsl_err("%s: payload b64 decode wrong\n", __func__);
goto bail;
}
/* parse the JOSE header */
if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE],
jws.map.len[LJWS_JOSE],
lws_concat_temp(temp, temp_len), &temp_len) < 0) {
lwsl_err("%s: JOSE parse failed\n", __func__);
goto bail;
}
/* confirm we used "es512" alg we expect from the JOSE hdr */
if (strcmp(jose.alg->alg, "ES512")) {
lwsl_err("%s: JOSE header has wrong alg\n", __func__);
goto bail;
}
jws.jwk = &jwk;
jws.context = context;
/* import the es512 jwk */
if (lws_jwk_import(&jwk, NULL, NULL, es512_jwk, strlen(es512_jwk))) {
lwsl_notice("%s: Failed to read JWK key\n", __func__);
goto bail;
}
/* sanity */
if (jwk.kty != LWS_GENCRYPTO_KTY_EC) {
lwsl_err("%s: kty: %d instead of EC\n",
__func__, jwk.kty);
goto bail1;
}
if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, &jwk, context) < 0) {
lwsl_notice("%s: confirm EC sig failed\n", __func__);
goto bail1;
}
/* A.3 "es512" RFC7515 worked example - sign */
l = strlen(es512_cser);
if (temp_len < l)
goto bail1;
p = lws_concat_temp(temp, temp_len);
memcpy(p, es512_cser, l + 1);
temp_len -= (l + 1);
/* scan the b64 compact serialization string to map the blocks */
if (lws_jws_b64_compact_map(p, l, &jws.map_b64) != 3)
goto bail1;
/* create the hash of the protected b64 part */
if (lws_genhash_init(&hash_ctx, jose.alg->hash_type) ||
lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_JOSE],
jws.map_b64.len[LJWS_JOSE]) ||
lws_genhash_update(&hash_ctx, ".", 1) ||
lws_genhash_update(&hash_ctx, jws.map_b64.buf[LJWS_PYLD],
jws.map_b64.len[LJWS_PYLD]) ||
lws_genhash_destroy(&hash_ctx, digest)) {
lws_genhash_destroy(&hash_ctx, NULL);
goto bail1;
}
/* overwrite the copy of the known b64 sig (it's placed inside buf) */
n = lws_jws_sign_from_b64(&jose, &jws,
(char *)jws.map_b64.buf[LJWS_SIG], 1024);
if (n < 0) {
lwsl_err("%s: failed signing test packet\n", __func__);
goto bail1;
}
jws.map_b64.len[LJWS_SIG] = n;
/* 2.4: confirm our generated signature can be verified */
p[l] = '\0';
if (lws_jws_sig_confirm_compact_b64(p, l, &map, &jwk, context,
lws_concat_temp(temp, temp_len), &temp_len) < 0) {
lwsl_notice("%s: confirm our ECDSA sig failed\n", __func__);
goto bail1;
}
/* end */
ret = 0;
bail1:
lws_jwk_destroy(&jwk);
lws_jose_destroy(&jose);
bail:
lwsl_notice("%s: selftest %s\n", __func__, ret < 0 ? "FAIL" : "OK");
return ret;
}
int
test_jws(struct lws_context *context)
{
char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
const struct lws_jose_jwe_alg *jose_alg;
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_genhmac_ctx ctx;
struct lws_jwk jwk;
int n;
int n = 0;
/* Test 1: SHA256 on RFC7515 worked example */
n |= test_jws_none(context);
n |= test_jws_HS256(context);
n |= test_jws_RS256(context);
n |= test_jws_ES256(context);
n |= test_jws_ES512(context);
/* 1.1: decode the JWK oct key */
if (lws_jwk_import(&jwk, NULL, NULL, key_jwk, strlen(key_jwk)) < 0) {
lwsl_notice("Failed to decode JWK test key\n");
return -1;
}
if (jwk.kty != LWS_GENCRYPTO_KYT_OCT) {
lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
return -1;
}
/* 1.2: create JWS known hdr + known payload */
n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
if (n < 0)
goto bail;
if (strcmp(buf, test1_enc))
goto bail;
enc_ptr = p + 1; /* + 1 skips the . */
n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
if (n < 0)
goto bail;
if (strcmp(enc_ptr, test2_enc))
goto bail;
/* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].buf,
jwk.e[LWS_GENCRYPTO_OCT_KEYEL_K].len))
goto bail;
if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
goto bail_destroy_hmac;
lws_genhmac_destroy(&ctx, digest);
/* 1.4: append a base64 encode of the computed HMAC digest */
enc_ptr = p + 1; /* + 1 skips the . */
n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
if (n < 0)
goto bail;
if (strcmp(enc_ptr, hash_enc)) { /* check against known B64URL hash */
lwsl_err("%s: b64 enc of computed HMAC mismatches '%s' '%s'\n",
__func__, enc_ptr, hash_enc);
goto bail;
}
/* 1.5: Check we can agree the signature matches the payload */
if (lws_jws_confirm_sig(buf, p - buf, &jwk, context) < 0) {
lwsl_notice("confirm sig failed\n");
goto bail;
}
lws_jwk_destroy(&jwk); /* finished with the key from the first test */
/* Test 2: RSA256 on RFC7515 worked example */
/* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
if (lws_jwk_import(&jwk, NULL, NULL,
rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
lwsl_notice("%s: 2.2: Failed to read JWK key\n", __func__);
goto bail2;
}
if (jwk.kty != LWS_GENCRYPTO_KYT_RSA) {
lwsl_err("%s: 2.2: kty: %d instead of RSA\n", __func__, jwk.kty);
}
/* 2.2: check the signature on the test packet from RFC7515 A-1 */
if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
&jwk, context) < 0) {
lwsl_notice("%s: 2.2: confirm rsa sig failed\n", __func__);
goto bail;
}
/* 2.3: generate our own signature for a copy of the test packet */
memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
/* set p to second . */
p = strchr(buf + 1, '.');
p1 = strchr(p + 1, '.');
if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", &jose_alg)) {
lwsl_err("%s: RSA1_5 not supported\n", __func__);
goto bail;
}
n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
p1 + 1, sizeof(buf) - (p1 - buf) - 1,
jose_alg, &jwk, context);
if (n < 0) {
lwsl_err("%s: failed signing test packet\n", __func__);
goto bail;
}
// puts(buf);
/* 2.4: confirm our signature can be verified */
if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk, context) < 0) {
lwsl_notice("confirm rsa sig 2 failed\n");
goto bail;
}
lws_jwk_destroy(&jwk);
/* end */
lwsl_notice("%s: selftest OK\n", __func__);
return 0;
bail_destroy_hmac:
lws_genhmac_destroy(&ctx, NULL);
bail:
lws_jwk_destroy(&jwk);
bail2:
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
return 1;
return n;
}

View file

@ -39,6 +39,8 @@ int main(int argc, const char **argv)
for (n = 0; n < 1000; n++) {
m = lwsac_use(&lwsac, sizeof(*m), 0);
if (!m)
return -1;
m->payload = n;
lws_list_ptr_insert(&list_head, &m->list_next, NULL);

View file

@ -0,0 +1,5 @@
|name|tests|
---|---
minimal-crypto-jwe|Examples for lws RFC7516 JWE apis
minimal-crypto-jwk|Examples for lws RFC7517 JWK apis

View file

@ -0,0 +1,77 @@
cmake_minimum_required(VERSION 2.8)
include(CheckCSourceCompiles)
set(SAMP lws-crypto-jwe)
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,69 @@
# lws minimal example for JWE
Demonstrates how to encrypt and decrypt using JWE and JWK, providing a
commandline tool for creating encrypted JWE and decoding them.
## build
```
$ cmake . && make
```
## usage
Stdin is either the plaintext (if encrypting) or JWE (if decrypting).
Stdout is either the JWE (if encrypting) or plaintext (if decrypting).
You must pass a private or public key JWK file in the -k option if encrypting,
and must pass a private key JWK file in the -k option if decrypting. To be
clear, for asymmetric keys the public part of the key is required to encrypt,
and the private part required to decrypt.
For convenience, a pair of public and private keys are provided,
`key-rsa-4096.private` and `key-rsa-4096.pub`, these were produced with just
```
$ lws-crypto-jwk -t RSA -b 4096 --public key-rsa-4096.pub >key-rsa-4096.private
```
Similar keys for EC modes may be produced with
```
$ lws-crypto-jwk -t EC -v P-256 --public key-ecdh-p-256.pub >key-ecdh-p-256.private
```
and for AES ("octet") symmetric keys
```
$ lws-crypto-jwk -t OCT -b 128 >key-aes-128.private
```
JWEs produced with openssl and mbedtls backends are completely interchangeable.
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-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
```
$ echo -n "plaintext0123456" | ./lws-crypto-jwe -k key-rsa-4096.private -e "RSA1_5 A128CBC-HS256"
[2018/12/19 16:20:25:6519] USER: LWS JWE example tool
[2018/12/19 16:20:25:6749] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
eyJhbGciOiJSU0ExXzUiLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.ivFr7qzx-pQ4V_edbjpdvR9OwWL9KmojPE2rXQM52oLtW0BtnxZu2_ezqhsAelyIcaworgfobs3u4bslXHMFbeJJjPb5xD0fBDe64OYXZH1NpUGTMJh9Ka4CrJ2B3xhxe7EByGAuGqmluqE0Yezj7rhSw7vlr5JAwuOJ8FaGa8aZ8ldki5G5h_S2Furlbjdcw3Rrxk7mCoMHcLoqzfZtggMPwGAMFogCqcwUo7oSLbBeGaa6hpMbfSysugseWdr8TzObQKPM52k6iVAlGwRaOg_qdLMgZiYRhHA6nFKTQd7XBbNY6qAS8sPuj7Zz344tF3RSfJ0zX_telG71sOtVv5fMpeDU-eCdpOWlCBfu6J6FQfAFu6SJryM4ajGOif09CwFI5qUQ33SOfQfS_M3nqSyd6Vu5M4lsDrb5wK7_XX5gqUwvI9wicf_8WWR-CQomRF-JvEASnA2SIf8QqYfa8R2rP9q6Md4vwO4EZrtxIsMDPsH-4ZEFu7vDjyy09QfIWWsnEb8-UgpVXensgt2m_2bZ76r1VB8-0nZLMwMyEhaH2wra9vX2FWao5UkmNJ7ht300f4_V6QzMFoePpwCvsufWBW6jcQLB-frCWe6uitWaZHEB4LxmNPKzQSz4QwwTKhpF1jNn8Xh1-w1m-2h0gj-oe-S8QBwPveqhPI1p2fI.snuhUTXHNu5mJ6dEPQqg6g.yl36qC4o0GE4nrquQ2YyCg.Vf0MoT7_kUrZdCNWXhq1DQ
```
Notice the logging is on stderr, and the output alone on stdout.
You can also pipe the output of the encrypt action directly into the decrypt
action, eg
```
$ echo -n "plaintext0123456" | \
./lws-crypto-jwe -k key-rsa-4096.pub -e "RSA1_5 A128CBC-HS256" | \
./lws-crypto-jwe -k key-rsa-4096.private
```
prints the plaintext on stdout.

View file

@ -0,0 +1 @@
{"d":"XcSl3ulvs4OGomu9thRPVQGOstim0PY7CibP_bnCmzjvmGmzb8J4q5AUmJCnZT5TesOzXuXhyG95CxQWsakd9GWHSAinV1QQSLsahaezPULRG1qmo37JqKb9noKkvXguh5XU5np8HjeoeeEkF_XqtCdEo0wHijEjTL9RZar98jmyAmlizoHIY9NnECavs4DZB27onU61B61vGpw-y4xhC9jlZSIwRqIMDzeTcSv8fRKcVYR80ozm2_KwWMpue27rS2EfTQUtsMXuYmnvMAf_DHqA0tNWyD1gpUWYHvlyBh5xnYrWPuXxQBRNesImQdRQl5VMMsuvdtY-uZfIVUdN5CcsB0acronx4UsmVg-Qz-jd1NVW4koZQM9uA4oWiMZg4FEUTQ-UWelHCldg-PYLAazsItmaHPF9LcAPkLkI8jaVS33v-DhSeXHW3Pg3sibtnPhouiSvD84zMtzu1gjFT7vtapMynBeZouqeWYT-BFeu2wzppJcW1YxTQ_Ai80VJSFY__Huw-9r1MOHmDRcEW7x9W97UezWDjrh5Shhh4C6SMYbaf7ouACzFu1i_r8Q06JqKA7aY8i5izKlKA0We9tQKlTF8Fgsneu9gpxFglvZsd1ersiA-MkuP9qTBQpyAf3kJ6HS9GrQUju6r3DExdWDjdvM5Grt8QD7Zkv-qXeE","dp":"M-LFs3T2GI1JxD5LJt2GgV4cMDKbiPKBddLukfG0duUxNp0-6x2LZ0ptxrlHrhxBMMmvCg4GEaujrZdaYWCar6xCnlnkVlOELz4yZ3JBSpS86thJw03xuE7lyeR7usFY4CpSqUQGI_YveITuFeoh4YjwdKDuqPhOpDI-34ptgU93dlBRS9nnQFTiVoUdP4bhGTKOpULTiLgPXHQxQR5rfiGVD9AIwqHvMdBQ0hxQBKEt37PbRWK_eTzMslHZGWNfbg8ipwJxisvHyUn0c1X3Uelw8BRyvNVCNovNDeCj-R7kFkMvriMd_sqGVy1Go46WZ2wMkUJHkvmYk0gDlhnTGQ","dq":"qO89nQEJfdkaDtGGyD-sQE2Mm8p_PIPSpCmgMfpl8zgSOb4P9iqXBgpHyS7w10uY_UHt8KW6pY7ozy0y4Gu_f4Wk_rcXiYYdbuIhlFl0_nLI2mfFPGxr1xC64zfjjEaBr4zIJr_YzhvTpjZFtIdSAH5VG5Tv-2yUtCC2DnKnU2kzEkgUeSI6LHOEVhXqup7C0Kjiv9FJsLR0hiqwH4oLziqH7EVqVDvJI3yL1lhqoLKjAu1ogTDgH7hzSrqVhttnpwL8rDcgbtY6Q8C2csdN3Jt1ucgtGy-Yzgqf_QIULP3CRlqzDTvHrMe2A9cNAQ4dNsCbNAjW_MxxGKKWuWXAMQ","e":"AQAB","kid":"my kid","kty":"RSA","n":"2_YjG_D1sOWJxs6cohikupHf5WJfWSFfSCrnNZ7WR7AyTLnKZAF4VKyimMeJTLYYwCAXMDD5XmkF8VluI4O-hASUIJ7F9eDg3vO7nPwtkWa9lkqt-QyQZ_PjiOGpwetBLzrsaXsC9PvdVzrKXnjeNPsmmbC_Fx2cUn4H_9H_WfXi01VR75XFTBtxTrDY7hmpZHuFCFUOMCW9siTZRk9339Y6ORBznBs4jFbkGI1Pmc3op0o5f8S1gus9L81W5uyUrxfd-CkmJ6eWE8I36cfzI6irN2bhVhR_NXERUtS0QOEeJYlRJXqfYkxTMVlsXPl6zbYt__ZYLC6ZiUTCc6K2KmfGh7fihWbao4dyQW3Mq4kClhpIT0O01Y0r7sR1j4jTnFNqbmtPSl9lEMrfiUHfOLqRJo3qizQ-b6HLCDty1otFz8Q8gg0rD3copQ_zFrcTGwJGAv2Absraj7kp9EJXBqneCJ3dlRO8rzx7KB9Dsj-ygh3kZaubkPCeT1v4l_VUY2iGnK4vzIGKM7j56DQ97ZAi1Bb0y6GYSbrWB2_z0DKJu0fiU-NscbKplR68vgppUM6_iogrk48JEZg_kkTymniqbT3g7J_WeoZSx1Uu8ZHI3ysIFfUtFscOa2SJGlj1ds-lfk6Oqac_I8ahRqQeyVAEisZPmYIGSJajbJopJ4s","p":"_V4CwEjRd8Hv9-ncqGdB_vtReTIuHSWQzSx4al15J3VxvPFI2kxicNeQKyq3OAVT2kmCmUP3ETgCdwuKIgw_QbEc8qNxtS_KpM_KsuTe9a5jrQKpt8ctYhzELZfr_sy9UzUGJzr8glLjJ1IDX4x6_JAqYB_NhttP6bzgu5Dt-DKtRPNO1qZtfhrLIgmltpC2M6-AlAv-dyHSHck2VJIL84Hwk4FulozEYxop0dKuZdfM5Z1dZM8-ICo62O0zUKzoWxKmQcB9_gDZsxYaO6xZ9BLmaW6-WcPSEI6YDnPk8ptnk_Kbyc4kPW4Z3ASczxjaewBmfl2_lwkqkndFVptAeQ","q":"3j9DR6ZpKC3WrshSrxXFYAuT19Rlf6qQ_9uD_Fq7dIpTjCZdl01695Qx7UmujKoetutL3RMCpeRdZR-gCLVh8aMxpMuIc5fHC6HbhsdF-I7GoqO0DEJ6coS3n5Ey4EXL5uoLh4C3l67wBKfLmPW28bxxG2QAP59jncWXkrBQm_qbS5Qon8r7wj0tejG_tGdsPjhsFc9KdnkkBucT6MiEVpzpdwDlsn7bHpMsyPlNyc0fj5qYmRB-DN7rv5varaisBaVT0mLQdwKjBDVqNVnU2m5azPhY-2txvihHaI5_cLIsLLaqKMbB17UxGumuT_o8S03_h8-1syO3Ay87y9pPIw","qi":"JY2uUek6wPrp4fPcInX_5WdNlhyghcGVEvlqxs9iOEUeCtUc6d42n9tgiImMu605dQaigvNaH5y1pwDpLlmxUk0nOUVxqo9mv0Uw8WNXB88FyDb0fPbewLpn4Fskb8Umv6_OymJ1W814DRG-jq3sI5DsB7AjtqJQ22nP2Vs1bIrx5fUxuScwrMsWSrrjAx4Kr8-5eeSDqE-_c7DPZ_zSPYDoHaMeR2pZfNAq3mEbxp8jMukzh77rYZ3ffQEA6AyxFSCSCrxVozhP4ypQ0jAkXVWOlj4nuV6briIqlL3ZboydwsIolRwaPSgH6-bw03XS6Hb9DA0KHJKLun94N9n5kw","use":"enc"}

View file

@ -0,0 +1 @@
{"e":"AQAB","kid":"my kid","kty":"RSA","n":"2_YjG_D1sOWJxs6cohikupHf5WJfWSFfSCrnNZ7WR7AyTLnKZAF4VKyimMeJTLYYwCAXMDD5XmkF8VluI4O-hASUIJ7F9eDg3vO7nPwtkWa9lkqt-QyQZ_PjiOGpwetBLzrsaXsC9PvdVzrKXnjeNPsmmbC_Fx2cUn4H_9H_WfXi01VR75XFTBtxTrDY7hmpZHuFCFUOMCW9siTZRk9339Y6ORBznBs4jFbkGI1Pmc3op0o5f8S1gus9L81W5uyUrxfd-CkmJ6eWE8I36cfzI6irN2bhVhR_NXERUtS0QOEeJYlRJXqfYkxTMVlsXPl6zbYt__ZYLC6ZiUTCc6K2KmfGh7fihWbao4dyQW3Mq4kClhpIT0O01Y0r7sR1j4jTnFNqbmtPSl9lEMrfiUHfOLqRJo3qizQ-b6HLCDty1otFz8Q8gg0rD3copQ_zFrcTGwJGAv2Absraj7kp9EJXBqneCJ3dlRO8rzx7KB9Dsj-ygh3kZaubkPCeT1v4l_VUY2iGnK4vzIGKM7j56DQ97ZAi1Bb0y6GYSbrWB2_z0DKJu0fiU-NscbKplR68vgppUM6_iogrk48JEZg_kkTymniqbT3g7J_WeoZSx1Uu8ZHI3ysIFfUtFscOa2SJGlj1ds-lfk6Oqac_I8ahRqQeyVAEisZPmYIGSJajbJopJ4s"}

View file

@ -0,0 +1,264 @@
/*
* lws-crypto-jwe
*
* Copyright (C) 2018 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>
/*
* handles escapes and line wrapping suitable for use
* defining a C char array ( -c option )
*/
static void
format_c(const char *key)
{
const char *k = key;
int seq = 0;
while (*k) {
if (*k == '{') {
putchar('\"');
putchar('{');
putchar('\"');
putchar('\n');
putchar('\t');
putchar('\"');
k++;
seq = 0;
continue;
}
if (*k == '}') {
putchar('\"');
putchar('\n');
putchar('\"');
putchar('}');
putchar('\"');
putchar('\n');
k++;
seq = 0;
continue;
}
if (*k == '\"') {
putchar('\\');
putchar('\"');
seq += 2;
k++;
continue;
}
if (*k == ',') {
putchar(',');
putchar('\"');
putchar('\n');
putchar('\t');
putchar('\"');
k++;
seq = 0;
continue;
}
putchar(*k);
seq++;
if (seq >= 60) {
putchar('\"');
putchar('\n');
putchar('\t');
putchar(' ');
putchar('\"');
seq = 1;
}
k++;
}
}
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;
struct lws_context_creation_info info;
int temp_len = sizeof(temp);
struct lws_context *context;
struct lws_jose jose;
struct lws_jwk jwk;
struct lws_jws jws;
const char *p;
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
lws_set_log_level(logs, NULL);
lwsl_user("LWS JWE example tool\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;
}
lws_jose_init(&jose);
lws_jws_init(&jws, &jwk, context);
/* if encrypting, set the ciphers */
if ((p = lws_cmdline_option(argc, argv, "-e"))) {
char *sp = strchr(p, ' ');
if (!sp) {
lwsl_err("format: -e \"<cek cipher alg> "
"<payload enc alg>\", eg, "
"-e \"RSA1_5 A128CBC-HS256\"\n");
return 1;
}
*sp = '\0';
if (lws_gencrypto_jwe_alg_to_definition(p, &jose.alg)) {
lwsl_err("Unknown cipher alg %s\n", p);
return 1;
}
if (lws_gencrypto_jwe_enc_to_definition(sp + 1, &jose.enc_alg)) {
lwsl_err("Unknown payload enc alg %s\n", sp + 1);
return 1;
}
/* create JOSE header, also needed for output */
if (lws_jws_alloc_element(&jws.map, LJWS_JOSE,
lws_concat_temp(temp, temp_len),
&temp_len, strlen(p) +
strlen(sp + 1) + 16, 0)) {
lwsl_err("%s: temp space too small\n", __func__);
return 1;
}
jws.map.len[LJWS_JOSE] = lws_snprintf(
(char *)jws.map.buf[LJWS_JOSE], temp_len,
"{\"alg\":\"%s\",\"enc\":\"%s\"}", p, sp + 1);
enc = 1;
}
in = lws_concat_temp(temp, temp_len);
n = read(0, in, temp_len);
if (n < 0) {
lwsl_err("Problem reading from stdin\n");
return 1;
}
temp_len -= n;
/* grab the key */
if ((p = lws_cmdline_option(argc, argv, "-k"))) {
if (lws_jwk_load(&jwk, p, NULL, NULL)) {
lwsl_err("%s: problem loading JWK %s\n", __func__, p);
return 1;
}
} else {
lwsl_err("-k <jwk file> is required\n");
return 1;
}
if (enc) {
/* point CTXT to the plaintext we read from stdin */
jws.map.buf[LJWE_CTXT] = in;
jws.map.len[LJWE_CTXT] = n;
/*
* Create a random CEK and set EKEY to it
* CEK size is determined by hash / hmac size
*/
n = lws_gencrypto_bits_to_bytes(jose.enc_alg->keybits_fixed);
if (lws_jws_randomize_element(context, &jws.map, LJWE_EKEY,
lws_concat_temp(temp, temp_len),
&temp_len, n,
LWS_JWE_LIMIT_KEY_ELEMENT_BYTES)) {
lwsl_err("Problem getting random\n");
goto bail1;
}
/* perform the encryption of the CEK and the plaintext */
n = lws_jwe_encrypt(&jose, &jws,
lws_concat_temp(temp, temp_len),
&temp_len);
if (n < 0) {
lwsl_err("%s: lws_jwe_encrypt failed\n", __func__);
goto bail1;
}
/* output the JWE in compact form */
n = lws_jwe_write_compact(&jose, &jws, compact, sizeof(compact));
if (n < 0) {
lwsl_err("%s: lws_jwe_write_compact failed: %d\n",
__func__, n);
goto bail1;
}
if (lws_cmdline_option(argc, argv, "-c"))
format_c(compact);
else
if (write(1, compact, strlen(compact)) < 0) {
lwsl_err("Write stdout failed\n");
goto bail1;
}
} else {
/*
* converts a compact serialization to b64 + decoded maps
* held in jws
*/
if (lws_jws_compact_decode(in, n, &jws.map, &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
* (information on the ciphers) and the jws (plaintext and
* signature info)
*/
n = lws_jwe_auth_and_decrypt(&jose, &jws);
if (n < 0) {
lwsl_err("%s: lws_jwe_auth_and_decrypt failed\n",
__func__);
goto bail1;
}
/* if it's valid, dump the plaintext and return 0 */
if (write(1, jws.map.buf[LJWE_CTXT], jws.map.len[LJWE_CTXT]) < 0) {
lwsl_err("Write stdout failed\n");
goto bail1;
}
}
result = 0;
bail1:
lws_jws_destroy(&jws);
lws_jwk_destroy(&jwk);
lws_context_destroy(context);
return result;
}

View file

@ -0,0 +1,77 @@
cmake_minimum_required(VERSION 2.8)
include(CheckCSourceCompiles)
set(SAMP lws-crypto-jwk)
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,51 @@
# lws minimal example for JWK
Demonstrates how to generate and format any kind of supported new random JWK keys.
The full private key is output to stdout, a version of the key with the private
part removed and some metadata adapted can be saved to a file at the same time
using `--public <file>`. In the public form, `key_ops` and `use` elements are
adjusted to remove activities that require a private key.
Key elements are output in strict RFC7638 lexicographic order as required by
some applications.
Keys produced with openssl and mbedtls backends are completely interchangeable.
## build
```
$ cmake . && make
```
## usage
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-t <type>|RSA, OCT or EC
-b <bits>|For RSA and OCT, key size in bits
-v <curve>|For EC keys, the curve, eg, "P-384"... this implies the key bits
--kid "ID string"|Key identity string
--use "use[ use]"|Key use restriction (mutually exclusive with --key-ops): sig, enc
--key-ops "op[ op]"|Key valid operations (mutually exclusive with --use): sign, verify, encrypt, decrypt, wrapKey, unwrapKey, deriveKey, deriveBits
-c|Format the jwk as a linebroken C string
--public <filepath>|Only output the full, private key, not the public version first
For legibility the example uses -c, however this
```
$ ./lws-crypto-jwk -t EC -v P-256 --key-ops "sign verify" --public mykey.pub
[2018/12/18 20:19:29:6972] USER: LWS JWK example
[2018/12/18 20:19:29:7200] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
[2018/12/18 20:19:29:7251] NOTICE: lws_jwk_generate: generating ECDSA key on curve P-256
{"crv":"P-256","d":"eMKM_S4BTL2aiebZLqvxglufV2YX4b3_32DesgEUOaM","key_ops":["sign","verify"],"kty":"EC","x":"OWauiGGtJ60ZegtqlwETQlmO1exTZdWbT2VbUs4a1hg","y":"g_eNOlqPecbguVQArL6Fd4T5xZthBgipNCBypXubPos"}
```
The output in `mykey.pub` is:
```
{"crv":"P-256","key_ops":["verify"],"kty":"EC","x":"OWauiGGtJ60ZegtqlwETQlmO1exTZdWbT2VbUs4a1hg","y":"g_eNOlqPecbguVQArL6Fd4T5xZthBgipNCBypXubPos"}
```
Notice the logging goes out on stderr, the key data goes on stdout.

View file

@ -0,0 +1,187 @@
/*
* lws-crypto-jwk
*
* Copyright (C) 2018 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>
/*
* handles escapes and line wrapping suitable for use
* defining a C char array ( -c option )
*/
static int
format_c(int fd, const char *key)
{
const char *k = key;
int seq = 0;
while (*k) {
if (*k == '{') {
if (write(fd, "\"{\"\n\t\"", 6) < 6)
return -1;
k++;
seq = 0;
continue;
}
if (*k == '}') {
if (write(fd, "\"\n\"}\"\n", 6) < 6)
return -1;
k++;
seq = 0;
continue;
}
if (*k == '\"') {
if (write(fd, "\\\"", 2) < 2)
return -1;
seq += 2;
k++;
continue;
}
if (*k == ',') {
if (write(fd, ",\"\n\t\"", 5) < 5)
return -1;
k++;
seq = 0;
continue;
}
if (write(fd, k, 1) < 1)
return -1;
seq++;
if (seq >= 60) {
if (write(fd, "\"\n\t \"", 5) < 5)
return -1;
seq = 1;
}
k++;
}
return 0;
}
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_context *context;
const char *p;
int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
int bits = 4096;
enum lws_gencrypto_kty kty = LWS_GENCRYPTO_KTY_RSA;
struct lws_jwk jwk;
char key[32768];
const char *curve = "P-256";
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
lws_set_log_level(logs, NULL);
lwsl_user("LWS JWK example\n");
if ((p = lws_cmdline_option(argc, argv, "-b")))
bits = atoi(p);
if ((p = lws_cmdline_option(argc, argv, "-t"))) {
if (!strcmp(p, "RSA"))
kty = LWS_GENCRYPTO_KTY_RSA;
else
if (!strcmp(p, "OCT"))
kty = LWS_GENCRYPTO_KTY_OCT;
else
if (!strcmp(p, "EC"))
kty = LWS_GENCRYPTO_KTY_EC;
else {
lwsl_err("Unknown key type (must be "
"OCT, RSA or EC)\n");
return 1;
}
}
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;
}
if ((p = lws_cmdline_option(argc, argv, "-v")))
curve = p;
if (lws_jwk_generate(context, &jwk, kty, bits, curve)) {
lwsl_err("lws_jwk_generate failed\n");
return 1;
}
if ((p = lws_cmdline_option(argc, argv, "--kid")))
lws_jwk_strdup_meta(&jwk, JWK_META_KID, p, strlen(p));
if ((p = lws_cmdline_option(argc, argv, "--use")))
lws_jwk_strdup_meta(&jwk, JWK_META_USE, p, strlen(p));
if ((p = lws_cmdline_option(argc, argv, "--key-ops")))
lws_jwk_strdup_meta(&jwk, JWK_META_KEY_OPS, p, strlen(p));
if ((p = lws_cmdline_option(argc, argv, "--public")) &&
kty != LWS_GENCRYPTO_KTY_OCT) {
int fd;
/* public version */
if (lws_jwk_export(&jwk, 0, key, sizeof(key)) < 0) {
lwsl_err("lws_jwk_export failed\n");
return 1;
}
fd = open(p, LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600);
if (fd < 0) {
lwsl_err("Can't open public key file %s\n", p);
return 1;
}
if (lws_cmdline_option(argc, argv, "-c"))
format_c(fd, key);
else {
if (write(fd, key, strlen(key)) < 0) {
lwsl_err("Write public failed\n");
return 1;
}
}
close(fd);
}
/* private version */
if (lws_jwk_export(&jwk, 1, key, sizeof(key)) < 0) {
lwsl_err("lws_jwk_export failed\n");
return 1;
}
if (lws_cmdline_option(argc, argv, "-c")) {
if (format_c(1, key) < 0)
return 1;
} else
if (write(1, key, strlen(key)) < 0) {
lwsl_err("Write stdout failed\n");
return 1;
}
lws_jwk_destroy(&jwk);
lws_context_destroy(context);
return result;
}

View file

@ -0,0 +1,77 @@
cmake_minimum_required(VERSION 2.8)
include(CheckCSourceCompiles)
set(SAMP lws-crypto-jws)
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 JWS
Demonstrates how to sign and verify using compact JWS and JWK, providing a
commandline tool for signing and verifying stdin.
## build
```
$ cmake . && make
```
## usage
Stdin is either the plaintext (if signing) or compact JWS (if verifying).
Stdout is either the JWE (if encrypting) or plaintext (if decrypting).
You must pass a private or public key JWK file in the -k option if encrypting,
and must pass a private key JWK file in the -k option if decrypting. To be
clear, for asymmetric keys the public part of the key is required to encrypt,
and the private part required to decrypt.
For convenience, a pair of public and private keys are provided,
`key-rsa-4096.private` and `key-rsa-4096.pub`, these were produced with just
```
$ lws-crypto-jwk -t RSA -b 4096 --public key-rsa-4096.pub >key-rsa-4096.private
```
Similar keys for EC modes may be produced with
```
$ lws-crypto-jwk -t EC -v P-256 --public key-ecdh-p-256.pub >key-ecdh-p-256.private
```
JWSs produced with openssl and mbedtls backends are completely interchangeable.
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-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
```
$ echo -n "plaintext0123456" | ./lws-crypto-jws -s "ES256" -k ec-p256.private
[2018/12/19 16:20:25:6519] USER: LWS JWE example tool
[2018/12/19 16:20:25:6749] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
eyJhbGciOiJSU0ExXzUiLCAiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.ivFr7qzx-pQ4V_edbjpdvR9OwWL9KmojPE2rXQM52oLtW0BtnxZu2_ezqhsAelyIcaworgfobs3u4bslXHMFbeJJjPb5xD0fBDe64OYXZH1NpUGTMJh9Ka4CrJ2B3xhxe7EByGAuGqmluqE0Yezj7rhSw7vlr5JAwuOJ8FaGa8aZ8ldki5G5h_S2Furlbjdcw3Rrxk7mCoMHcLoqzfZtggMPwGAMFogCqcwUo7oSLbBeGaa6hpMbfSysugseWdr8TzObQKPM52k6iVAlGwRaOg_qdLMgZiYRhHA6nFKTQd7XBbNY6qAS8sPuj7Zz344tF3RSfJ0zX_telG71sOtVv5fMpeDU-eCdpOWlCBfu6J6FQfAFu6SJryM4ajGOif09CwFI5qUQ33SOfQfS_M3nqSyd6Vu5M4lsDrb5wK7_XX5gqUwvI9wicf_8WWR-CQomRF-JvEASnA2SIf8QqYfa8R2rP9q6Md4vwO4EZrtxIsMDPsH-4ZEFu7vDjyy09QfIWWsnEb8-UgpVXensgt2m_2bZ76r1VB8-0nZLMwMyEhaH2wra9vX2FWao5UkmNJ7ht300f4_V6QzMFoePpwCvsufWBW6jcQLB-frCWe6uitWaZHEB4LxmNPKzQSz4QwwTKhpF1jNn8Xh1-w1m-2h0gj-oe-S8QBwPveqhPI1p2fI.snuhUTXHNu5mJ6dEPQqg6g.yl36qC4o0GE4nrquQ2YyCg.Vf0MoT7_kUrZdCNWXhq1DQ
```
Notice the logging is on stderr, and the output alone on stdout.
When signing, the compact representation of the JWS is output on stdout.
When verifying, if the signature is valid the plaintext is output on stdout
and the tool exits with a 0 exit code. Otherwise nothing is output on stdout
and it exits with a nonzero exit code.

View file

@ -0,0 +1,185 @@
/*
* lws-crypto-jws
*
* Copyright (C) 2018 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>
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;
struct lws_context_creation_info info;
struct lws_jws_compact_map map;
int temp_len = sizeof(temp);
struct lws_context *context;
struct lws_jose jose;
struct lws_jwk jwk;
struct lws_jws jws;
const char *p;
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
lws_set_log_level(logs, NULL);
lwsl_user("LWS JWS example tool\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;
}
lws_jose_init(&jose);
lws_jws_init(&jws, &jwk, context);
/* if signing, set the ciphers */
if ((p = lws_cmdline_option(argc, argv, "-s"))) {
if (lws_gencrypto_jws_alg_to_definition(p, &jose.alg)) {
lwsl_err("format: -s \"<jws cipher alg>\", eg, "
"-e \"RS256\"\n");
return 1;
}
/* create JOSE header, also needed for output */
if (lws_jws_alloc_element(&jws.map, LJWS_JOSE,
lws_concat_temp(temp, temp_len),
&temp_len, strlen(p) + 10, 0)) {
lwsl_err("%s: temp space too small\n", __func__);
return 1;
}
jws.map.len[LJWS_JOSE] =
lws_snprintf((char *)jws.map.buf[LJWS_JOSE],
temp_len, "{\"alg\":\"%s\"}", p);
sign = 1;
}
in = lws_concat_temp(temp, temp_len);
n = read(0, in, temp_len);
if (n < 0) {
lwsl_err("Problem reading from stdin\n");
return 1;
}
temp_len -= n;
/* grab the key */
if ((p = lws_cmdline_option(argc, argv, "-k"))) {
if (lws_jwk_load(&jwk, p, NULL, NULL)) {
lwsl_err("%s: problem loading JWK %s\n", __func__, p);
return 1;
}
} else {
lwsl_err("-k <jwk file> is required\n");
return 1;
}
if (sign) {
/* add the plaintext from stdin to the map and a b64 version */
jws.map.buf[LJWS_PYLD] = in;
jws.map.len[LJWS_PYLD] = n;
if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_PYLD,
lws_concat_temp(temp, temp_len),
&temp_len, jws.map.buf[LJWS_PYLD],
jws.map.len[LJWS_PYLD]))
goto bail1;
/* add the b64 JOSE header to the b64 map */
if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_JOSE,
lws_concat_temp(temp, temp_len),
&temp_len, jws.map.buf[LJWS_JOSE],
jws.map.len[LJWS_JOSE]))
goto bail1;
/* prepare the space for the b64 signature in the map */
if (lws_jws_alloc_element(&jws.map, LJWS_SIG,
lws_concat_temp(temp, temp_len),
&temp_len, lws_base64_size(
LWS_JWE_LIMIT_KEY_ELEMENT_BYTES), 0)) {
lwsl_err("%s: temp space too small\n", __func__);
goto bail1;
}
/* sign the plaintext */
n = lws_jws_sign_from_b64(&jose, &jws,
(char *)jws.map_b64.buf[LJWS_SIG],
jws.map_b64.len[LJWS_SIG]);
if (n < 0) {
lwsl_err("%s: failed signing test packet\n", __func__);
goto bail1;
}
/* 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 (n < 0) {
lwsl_notice("%s: write_compact failed\n", __func__);
goto bail1;
}
/* dump the compact JWS representation on stdout */
if (write(1, compact, strlen(compact)) < 0) {
lwsl_err("Write stdout failed\n");
goto bail1;
}
} 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;
}
lwsl_notice("VALID\n");
/* dump the verifed plaintext and return 0 */
if (write(1, jws.map.buf[LJWS_PYLD], jws.map.len[LJWS_PYLD]) < 0) {
lwsl_err("Write stdout failed\n");
goto bail1;
}
}
result = 0;
bail1:
lws_jws_destroy(&jws);
lws_jwk_destroy(&jwk);
lws_context_destroy(context);
return result;
}

View file

@ -91,9 +91,11 @@ int main(int argc, const char **argv)
info.mounts = &mount;
info.options =
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
if ((p = lws_cmdline_option(argc, argv, "-t")))
if ((p = lws_cmdline_option(argc, argv, "-t"))) {
info.count_threads = atoi(p);
else
if (info.count_threads < 1 || info.count_threads > LWS_MAX_SMP)
return 1;
} else
info.count_threads = COUNT_THREADS;
if (lws_cmdline_option(argc, argv, "-s")) {

View file

@ -439,7 +439,7 @@ lws_acme_load_create_auth_keys(struct per_vhost_data__lws_acme_client *vhd,
NULL, NULL))
return 0;
vhd->jwk.kty = LWS_GENCRYPTO_KYT_RSA;
vhd->jwk.kty = LWS_GENCRYPTO_KTY_RSA;
lwsl_notice("Generating ACME %d-bit keypair... "
"will take a little while\n", bits);
n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, LGRSAM_PKCS1_1_5,
@ -549,17 +549,19 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason,
*end = buf + sizeof(buf) - 1, digest[32], *failreason = NULL;
const struct lws_protocol_vhost_options *pvo;
struct lws_acme_cert_aging_args *caa;
const struct lws_jose_jwe_alg *args;
struct acme_connection *ac = NULL;
struct lws_genhash_ctx hctx;
unsigned char **pp, *pend;
const char *content_type;
struct lws_jose jose;
struct lws *cwsi;
int n, m;
if (vhd)
ac = vhd->ac;
lws_jose_init(&jose);
switch ((int)reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
@ -783,12 +785,12 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason,
puts(start);
pkt_add_hdrs:
if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", &args)) {
if (lws_gencrypto_jwe_alg_to_definition("RSA1_5", &jose.alg)) {
ac->len = 0;
lwsl_notice("%s: no RSA1_5\n", __func__);
goto failed;
}
ac->len = lws_jwe_create_packet(&vhd->jwk, args,
ac->len = lws_jwe_create_packet(&jose, &vhd->jwk,
start, p - start,
ac->replay_nonce,
&ac->buf[LWS_PRE],

View file

@ -159,7 +159,7 @@ scan_upload_dir(struct vhd_deaddrop *vhd)
p += lws_snprintf(p, (filepath + sizeof(filepath)) - p,
"%s/", subdir[m]);
p += lws_snprintf(p, (filepath + sizeof(filepath)) - p, "%s",
lws_snprintf(p, (filepath + sizeof(filepath)) - p, "%s",
de->d_name);
/* ignore temp files */
@ -304,12 +304,14 @@ file_upload_cb(void *data, const char *name, const char *filename,
if (state == LWS_UFS_CONTENT)
break;
close((int)(long long)pss->fd);
if ((int)(long long)pss->fd >= 0)
close((int)(long long)pss->fd);
/* the temp filename without the ~ */
lws_strncpy(filename2, pss->filename, sizeof(filename2));
filename2[strlen(filename2) - 1] = '\0';
rename(pss->filename, filename2);
if (rename(pss->filename, filename2) < 0)
lwsl_err("%s: unable to rename\n", __func__);
pss->fd = LWS_INVALID_FILE;
pss->response_code = HTTP_STATUS_OK;

View file

@ -249,7 +249,7 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_CLI_ADOPT:
lwsl_debug("LWS_CALLBACK_RAW_CLI_ADOPT: pss %p\n", pss);
if (conn)
if (conn || !pss)
break;
conn = pss->conn = lws_get_opaque_user_data(wsi);
conn->established[ONW] = 1;
@ -279,6 +279,9 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_CLI_RX:
lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_RX: %d\n", (int)len);
if (!conn)
return 0;
if (!pss || !conn->wsi[ACC] || conn->closed[ACC]) {
lwsl_info(" pss %p, wsi[ACC] %p, closed[ACC] %d\n",
pss, conn->wsi[ACC], conn->closed[ACC]);
@ -313,6 +316,9 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE:
lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE\n");
if (!conn)
break;
ppkt = lws_ring_get_element(conn->r[ACC], &conn->t[ACC]);
if (!ppkt) {
lwsl_info("%s: CLI_WRITABLE had nothing in acc ring\n",
@ -374,7 +380,8 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_SRV_ADOPT:
lwsl_debug("LWS_CALLBACK_RAW_SRV_ADOPT\n");
if (!pss)
return -1;
conn = pss->conn = malloc(sizeof(struct conn));
if (!pss->conn)
return -1;
@ -463,7 +470,7 @@ bad_onward:
case LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE:
lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE\n");
if (!conn->established[ONW] || conn->closed[ONW])
if (!conn || !conn->established[ONW] || conn->closed[ONW])
break;
ppkt = lws_ring_get_element(conn->r[ONW], &conn->t[ONW]);

View file

@ -86,18 +86,6 @@ lws_buf(uint8_t **p, void *s, uint32_t len)
return 0;
}
int
lws_timingsafe_bcmp(const void *a, const void *b, uint32_t len)
{
const uint8_t *pa = a, *pb = b;
uint8_t sum = 0;
while (len--)
sum |= (*pa++ ^ *pb++);
return sum;
}
void
write_task(struct per_session_data__sshd *pss, struct lws_ssh_channel *ch,
int task)
@ -1250,7 +1238,8 @@ again:
e[LWS_GENCRYPTO_RSA_KEYEL_N].len = m;
if (lws_genrsa_create(&ctx, e, pss->vhd->context,
LGRSAM_PKCS1_1_5))
LGRSAM_PKCS1_1_5,
LWS_GENHASH_TYPE_UNKNOWN))
goto ua_fail;
/*

View file

@ -10,7 +10,7 @@
set -u
PARALLEL=8
PARALLEL=2
N=1
OS=`uname`

View file

@ -342,7 +342,7 @@ ssh_ops_is_pubkey_authorized(const char *username, const char *type,
* <len32>E<len32>N that the peer sends us
*/
if (memcmp(peer, ps, peer_len)) {
if (lws_timingsafe_bcmp(peer, ps, peer_len)) {
lwsl_info("factors mismatch\n");
goto bail;
}