mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
jwe
This commit is contained in:
parent
a3dcc95471
commit
eda102e397
81 changed files with 8280 additions and 1631 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -51,3 +51,4 @@ doc
|
|||
/build3/
|
||||
/cov-int/
|
||||
/.vs/
|
||||
/build-mtls/
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
48
README.md
48
README.md
|
@ -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.
|
||||
|
||||

|
||||

|
||||
|
||||
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`
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
======
|
||||
|
||||
|
|
|
@ -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
BIN
doc-assets/lws-overview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 507 KiB |
|
@ -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 |
|
@ -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>
|
||||
|
||||
|
|
|
@ -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:
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
///@}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
//@}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
192
lib/jose/jwe/jwe-aeskw.c
Normal 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];
|
||||
}
|
||||
|
||||
|
437
lib/jose/jwe/jwe-rsa-aescbc.c
Normal file
437
lib/jose/jwe/jwe-rsa-aescbc.c
Normal 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];
|
||||
}
|
349
lib/jose/jwe/jwe-rsa-aesgcm.c
Normal file
349
lib/jose/jwe/jwe-rsa-aesgcm.c
Normal 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];
|
||||
}
|
|
@ -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
50
lib/jose/jwe/private.h
Normal 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);
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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--;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
5
minimal-examples/crypto/README.md
Normal file
5
minimal-examples/crypto/README.md
Normal 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
|
||||
|
77
minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt
Normal file
77
minimal-examples/crypto/minimal-crypto-jwe/CMakeLists.txt
Normal 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()
|
69
minimal-examples/crypto/minimal-crypto-jwe/README.md
Normal file
69
minimal-examples/crypto/minimal-crypto-jwe/README.md
Normal 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.
|
|
@ -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"}
|
|
@ -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"}
|
264
minimal-examples/crypto/minimal-crypto-jwe/main.c
Normal file
264
minimal-examples/crypto/minimal-crypto-jwe/main.c
Normal 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;
|
||||
}
|
77
minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt
Normal file
77
minimal-examples/crypto/minimal-crypto-jwk/CMakeLists.txt
Normal 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()
|
51
minimal-examples/crypto/minimal-crypto-jwk/README.md
Normal file
51
minimal-examples/crypto/minimal-crypto-jwk/README.md
Normal 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.
|
187
minimal-examples/crypto/minimal-crypto-jwk/main.c
Normal file
187
minimal-examples/crypto/minimal-crypto-jwk/main.c
Normal 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;
|
||||
}
|
77
minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt
Normal file
77
minimal-examples/crypto/minimal-crypto-jws/CMakeLists.txt
Normal 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()
|
59
minimal-examples/crypto/minimal-crypto-jws/README.md
Normal file
59
minimal-examples/crypto/minimal-crypto-jws/README.md
Normal 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.
|
||||
|
185
minimal-examples/crypto/minimal-crypto-jws/main.c
Normal file
185
minimal-examples/crypto/minimal-crypto-jws/main.c
Normal 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;
|
||||
}
|
|
@ -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")) {
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
set -u
|
||||
|
||||
PARALLEL=8
|
||||
PARALLEL=2
|
||||
N=1
|
||||
OS=`uname`
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue