mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
JOSE: refactor and prepare for JWE
Until now the JOSE pieces only had enough support for ACME. This patch improves the JWK parsing to prepare for more complete support and for adding JWE, genaes and genec in later patches.
This commit is contained in:
parent
5976e4b334
commit
440dacc992
38 changed files with 2851 additions and 1093 deletions
|
@ -106,8 +106,10 @@ 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_GENEC "Enable support for Generic EC (EC with api 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)
|
||||
|
@ -156,6 +158,9 @@ 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)
|
||||
endif()
|
||||
|
||||
# do you care about this? Then send me a patch where it disables it on travis
|
||||
|
@ -295,10 +300,15 @@ if (LWS_WITH_ACME)
|
|||
set (LWS_WITH_JWS 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_JWE)
|
||||
set(LWS_WITH_JWS 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_JWS)
|
||||
set(LWS_WITH_LEJP 1)
|
||||
set(LWS_WITH_GENHASH 1)
|
||||
set(LWS_WITH_GENRSA 1)
|
||||
set(LWS_WITH_GENEC 1)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV)
|
||||
|
@ -1021,6 +1031,11 @@ if (LWS_WITH_SSL)
|
|||
lib/tls/mbedtls/lws-genrsa.c
|
||||
)
|
||||
endif()
|
||||
if (LWS_WITH_GENEC)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/mbedtls/lws-genec.c
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
lib/tls/openssl/ssl.c
|
||||
|
@ -1035,6 +1050,11 @@ if (LWS_WITH_SSL)
|
|||
lib/tls/openssl/lws-genrsa.c
|
||||
)
|
||||
endif()
|
||||
if (LWS_WITH_GENEC)
|
||||
list(APPEND SOURCES
|
||||
lib/tls/openssl/lws-genec.c
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT LWS_WITHOUT_SERVER)
|
||||
|
@ -1191,8 +1211,14 @@ endif()
|
|||
|
||||
if (LWS_WITH_JWS)
|
||||
list(APPEND SOURCES
|
||||
lib/misc/jws/jwk.c
|
||||
lib/misc/jws/jws.c)
|
||||
lib/jose/jwk/jwk.c
|
||||
lib/jose/jws/jose.c
|
||||
lib/jose/jws/jws.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_JWE)
|
||||
list(APPEND SOURCES
|
||||
lib/jose/jwe/jwe.c)
|
||||
endif()
|
||||
|
||||
# Add helper files for Windows.
|
||||
|
|
40
READMEs/README.crypto-apis.md
Normal file
40
READMEs/README.crypto-apis.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Lws Crypto Apis
|
||||
|
||||
## Overview
|
||||
|
||||

|
||||
|
||||
Lws provides a "generic" crypto layer on top of both OpenSSL and
|
||||
compatible tls library, and mbedtls. Using this layer, your code
|
||||
can work without any changes on both types of tls library crypto
|
||||
backends... it's as simple as rebuilding lws with `-DLWS_WITH_MBEDTLS=0`
|
||||
or `=1` at cmake.
|
||||
|
||||
The generic layer can be used directly (as in, eg, the sshd plugin),
|
||||
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).
|
||||
|
||||
## Using the generic layer
|
||||
|
||||
All the necessary includes are part of `libwebsockets.h`.
|
||||
|
||||
|api|cmake|header|Functionality|
|
||||
|---|---|---|---|
|
||||
|genhash|`LWS_WITH_GENHASH`|[./include/libwebsockets/lws-genhash.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genhash.h)|Provides SHA1 + SHA2 hashes and hmac|
|
||||
|genrsa|`LWS_WITH_GENRSA`|[./include/libwebsockets/lws-genrsa.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-genrsa.h)|Provides RSA encryption, decryption, signing, verification, key generation and creation|
|
||||
|
||||
Unit tests for these apis, which serve as usage examples, can be found in [./minimal-examples/api-tests/api-test-gencrypto](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-gencrypto)
|
||||
|
||||
## Using the JOSE layer
|
||||
|
||||
All the necessary includes are part of `libwebsockets.h`.
|
||||
|
||||
|api|cmake|header|Functionality|
|
||||
|---|---|---|---|
|
||||
|JOSE|`LWS_WITH_JWS`|[./include/libwebsockets/jose.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jose.h)|Provides signature and verifcation services for RFC7515 JOSE JSON|
|
||||
|JWS|`LWS_WITH_JWS`|[./include/libwebsockets/jws.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jws.h)|Provides signature and verifcation services for RFC7515 JWS JSON|
|
||||
|JWK|`LWS_WITH_JWS`|[./include/libwebsockets/jwk.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-jwk.h)|Provides signature and verifcation services for RFC7517 JWK JSON, both "keys" arrays and singletons|
|
||||
|
||||
Unit tests for these apis, which serve as usage examples, can be found in [./minimal-examples/api-tests/api-test-jose](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-jose)
|
||||
|
|
@ -75,6 +75,7 @@
|
|||
#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_LEJP
|
||||
#cmakedefine LWS_WITH_LIBEV
|
||||
|
|
43
doc-assets/lws-crypto-overview.svg
Normal file
43
doc-assets/lws-crypto-overview.svg
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="200.21mm" height="105.23mm" version="1.1" viewBox="0 0 200.21263 105.22723" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="a" x="-.036673" y="-.074724" width="1.0733" height="1.1494" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="2.8502899"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g transform="translate(651.2 344.5)">
|
||||
<g fill-opacity=".99606">
|
||||
<rect x="-644.35" y="-337.66" width="186.53" height="91.546" filter="url(#a)"/>
|
||||
<rect x="-645.81" y="-339.25" width="186.53" height="91.546" fill="#fff"/>
|
||||
<rect x="-633.9" y="-327.61" width="105.3" height="41.275" fill="#f6ffd5" stroke="#e9ddaf" stroke-dasharray="2.33000003, 2.33000003" stroke-linejoin="round" stroke-width="1.165"/>
|
||||
</g>
|
||||
<g fill-opacity=".99606">
|
||||
<rect x="-583.53" y="-312" width="5.4256" height="9.5415"/>
|
||||
<rect x="-554.9" y="-292.8" width="5.4256" height="13.996"/>
|
||||
<rect x="-610.37" y="-292.94" width="5.4256" height="13.996"/>
|
||||
</g>
|
||||
<g fill-opacity=".99606">
|
||||
<rect x="-632.82" y="-279.84" width="48.643" height="24.322" fill="#8a0"/>
|
||||
<rect x="-576.51" y="-280.03" width="48.643" height="24.322" fill="#a80"/>
|
||||
<rect x="-628.51" y="-303.48" width="94.328" height="12.348" fill="#008000"/>
|
||||
<rect x="-628.26" y="-323.5" width="94.063" height="12.348" fill="#00aad4"/>
|
||||
</g>
|
||||
<g fill="#000000" font-family="'Open Sans'" letter-spacing="0px" text-anchor="middle" word-spacing="0px">
|
||||
<text x="-608.50153" y="-268.05179" dominant-baseline="auto" font-size="7.7611px" stroke-width=".26458" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-608.50153" y="-268.05179" stroke-width=".26458">OpenSSL</tspan></text>
|
||||
<text x="-552.70789" y="-264.67337" dominant-baseline="auto" font-size="7.7611px" stroke-width=".26458" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-552.70789" y="-264.67337" stroke-width=".26458">mbedTLS</tspan></text>
|
||||
<text x="-608.09796" y="-261.13004" dominant-baseline="auto" font-size="4.8053px" stroke-width=".16382" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-608.09796" y="-261.13004" stroke-width=".16382">and derivitives</tspan></text>
|
||||
</g>
|
||||
<text x="-580.06641" y="-296.07693" dominant-baseline="auto" fill="#ffffff" font-family="'Open Sans'" font-size="5.936px" letter-spacing="0px" stroke-width=".20236" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-580.06641" y="-296.07693" fill="#ffffff" stroke-width=".20236">genhash, genrsa, genaes, genec</tspan></text>
|
||||
<text x="-581.93494" y="-314.70865" dominant-baseline="auto" fill="#ffffff" font-family="'Open Sans'" font-size="7.7611px" letter-spacing="0px" stroke-width=".26458" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-581.93494" y="-314.70865" fill="#ffffff" stroke-width=".26458">JOSE, JWS, JWK, JWE</tspan></text>
|
||||
<g fill="#000000" font-family="'Open Sans'" font-size="4.3141px" letter-spacing="0px" stroke-width=".14707" text-anchor="middle" word-spacing="0px">
|
||||
<text x="-493.83551" y="-274.2438" dominant-baseline="auto" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-493.83551" y="-274.2438">TLS-library-specific</tspan><tspan x="-493.83551" y="-268.8512">and cipher-specific</tspan><tspan x="-493.83551" y="-263.45856">keys using EVP</tspan><tspan x="-493.83551" y="-258.06595">or bignum</tspan></text>
|
||||
<text x="-493.41776" y="-301.00821" dominant-baseline="auto" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-493.41776" y="-301.00821">TLS library-independent</tspan><tspan x="-493.41776" y="-295.6156">metadata +</tspan><tspan x="-493.41776" y="-290.22296">binary key elements</tspan></text>
|
||||
<text x="-493.22754" y="-319.46451" dominant-baseline="auto" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-493.22754" y="-319.46451">JWK JSON key</tspan><tspan x="-493.22754" y="-314.0719">creation and parsing</tspan></text>
|
||||
</g>
|
||||
<g>
|
||||
<path d="m-492.48-306.99-1.2324 1.0579-1.247-0.9926 0.57189-0.052 0.0645-4.34h-0.66758l1.374-1.2497 1.2493 1.3264-0.66727 0.053 0.0298 4.1963z" fill="#0b2822"/>
|
||||
<path d="m-492.34-280.9-1.2324 1.0579-1.247-0.9926 0.57189-0.052 0.0645-4.34h-0.66758l1.374-1.2497 1.2493 1.3264-0.66727 0.053 0.0298 4.1963z" fill="#0b2822"/>
|
||||
<text x="-618.2807" y="-330.22464" dominant-baseline="auto" fill="#000000" font-family="'Open Sans'" font-size="5.0105px" letter-spacing="0px" stroke-width=".17081" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-618.2807" y="-330.22464" stroke-width=".17081">libwebsockets</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.8 KiB |
|
@ -422,9 +422,11 @@ struct lws;
|
|||
#endif
|
||||
|
||||
#include <libwebsockets/lws-genhash.h>
|
||||
#include <libwebsockets/lws-genrsa.h>
|
||||
#include <libwebsockets/lws-jwk.h>
|
||||
#include <libwebsockets/lws-jose.h>
|
||||
#include <libwebsockets/lws-jws.h>
|
||||
#include <libwebsockets/lws-genrsa.h>
|
||||
#include <libwebsockets/lws-genec.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
|
34
include/libwebsockets/lws-genec.h
Normal file
34
include/libwebsockets/lws-genec.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* libwebsockets - Generic Elliptic Curve Encryption
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* 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
|
||||
*
|
||||
* included from libwebsockets.h
|
||||
*/
|
||||
|
||||
/* include/libwebsockets/lws-jwk.h must be included before this */
|
||||
|
||||
struct lws_genec_ctx {
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
mbedtls_rsa_context *ctx;
|
||||
#else
|
||||
BIGNUM *bn[LWS_COUNT_EC_KEY_ELEMENTS];
|
||||
RSA *rsa;
|
||||
#endif
|
||||
};
|
||||
|
|
@ -32,44 +32,23 @@
|
|||
*/
|
||||
///@{
|
||||
|
||||
enum enum_jwk_tok {
|
||||
JWK_KEY_E,
|
||||
JWK_KEY_N,
|
||||
JWK_KEY_D,
|
||||
JWK_KEY_P,
|
||||
JWK_KEY_Q,
|
||||
JWK_KEY_DP,
|
||||
JWK_KEY_DQ,
|
||||
JWK_KEY_QI,
|
||||
JWK_KTY, /* also serves as count of real elements */
|
||||
JWK_KEY,
|
||||
};
|
||||
|
||||
#define LWS_COUNT_RSA_ELEMENTS JWK_KTY
|
||||
/* include/libwebsockets/lws-jwk.h must be included before this */
|
||||
|
||||
struct lws_genrsa_ctx {
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
mbedtls_rsa_context *ctx;
|
||||
#else
|
||||
BIGNUM *bn[LWS_COUNT_RSA_ELEMENTS];
|
||||
BIGNUM *bn[LWS_COUNT_RSA_KEY_ELEMENTS];
|
||||
RSA *rsa;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct lws_genrsa_element {
|
||||
uint8_t *buf;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
struct lws_genrsa_elements {
|
||||
struct lws_genrsa_element e[LWS_COUNT_RSA_ELEMENTS];
|
||||
struct lws_context *context;
|
||||
};
|
||||
|
||||
/** lws_jwk_destroy_genrsa_elements() - Free allocations in genrsa_elements
|
||||
*
|
||||
* \param el: your struct lws_genrsa_elements
|
||||
* \param el: your struct lws_jwk_elements
|
||||
*
|
||||
* This is a helper for user code making use of struct lws_genrsa_elements
|
||||
* This is a helper for user code making use of struct lws_jwk_elements
|
||||
* where the elements are allocated on the heap, it frees any non-NULL
|
||||
* buf element and sets the buf to NULL.
|
||||
*
|
||||
|
@ -77,7 +56,7 @@ struct lws_genrsa_elements {
|
|||
* creation and destruction themselves.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el);
|
||||
lws_jwk_destroy_genrsa_elements(struct lws_jwk_elements *el);
|
||||
|
||||
/** lws_genrsa_public_decrypt_create() - Create RSA public decrypt context
|
||||
*
|
||||
|
@ -92,7 +71,8 @@ lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el);
|
|||
* This and related APIs operate identically with OpenSSL or mbedTLS backends.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el);
|
||||
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_jwk_elements *el,
|
||||
struct lws_context *context);
|
||||
|
||||
/** lws_genrsa_new_keypair() - Create new RSA keypair
|
||||
*
|
||||
|
@ -110,7 +90,24 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el);
|
|||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
||||
struct lws_genrsa_elements *el, int bits);
|
||||
struct lws_jwk_elements *el, int bits);
|
||||
|
||||
/** lws_genrsa_public_encrypt() - Perform RSA public 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_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
|
||||
*
|
||||
|
@ -120,7 +117,7 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
|||
* \param out: decrypted output
|
||||
* \param out_max: size of output buffer
|
||||
*
|
||||
* Performs the decryption.
|
||||
* Performs PKCS1 v1.5 Decryption
|
||||
*
|
||||
* Returns <0 for error, or length of decrypted data.
|
||||
*
|
||||
|
|
71
include/libwebsockets/lws-jose.h
Normal file
71
include/libwebsockets/lws-jose.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* 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
|
||||
*
|
||||
* included from libwebsockets.h
|
||||
*/
|
||||
|
||||
enum lws_jws_jose_hdr_indexes {
|
||||
LJJHI_ALG, /* REQUIRED */
|
||||
LJJHI_JKU, /* Optional: string */
|
||||
LJJHI_JWK, /* Optional: jwk JSON object: public key: */
|
||||
LJJHI_KID, /* Optional: string */
|
||||
LJJHI_X5U, /* Optional: string: url of public key cert / chain */
|
||||
LJJHI_X5C, /* Optional: base64 (NOT -url): actual cert */
|
||||
LJJHI_X5T, /* Optional: base64url: SHA-1 of actual cert */
|
||||
LJJHI_X5T_S256, /* Optional: base64url: SHA-256 of actual cert */
|
||||
LJJHI_TYP, /* Optional: string: media type */
|
||||
LJJHI_CTY, /* Optional: string: content media type */
|
||||
LJJHI_CRIT, /* Optional for send, REQUIRED: array of strings:
|
||||
* mustn't contain standardized strings or null set */
|
||||
|
||||
LJJHI_ENC, /* JWE only: Optional: string */
|
||||
LJJHI_ZIP, /* JWE only: Optional: string ("DEF" = deflate) */
|
||||
|
||||
LJJHI_EPK, /* Additional arg for JWE ECDH: ephemeral public key */
|
||||
LJJHI_APU, /* Additional arg for JWE ECDH: base64url */
|
||||
LJJHI_APV, /* Additional arg for JWE ECDH: base64url */
|
||||
LJJHI_IV, /* Additional arg for JWE AES: base64url */
|
||||
LJJHI_TAG, /* Additional arg for JWE AES: base64url */
|
||||
LJJHI_P2S, /* Additional arg for JWE PBES2: base64url: salt */
|
||||
LJJHI_P2C, /* Additional arg for JWE PBES2: integer: count */
|
||||
|
||||
LWS_COUNT_JOSE_HDR_ELEMENTS
|
||||
};
|
||||
|
||||
struct lws_jose {
|
||||
/* jose header elements */
|
||||
struct lws_jwk_elements e[LWS_COUNT_JOSE_HDR_ELEMENTS];
|
||||
};
|
||||
|
||||
enum lws_jws_algtype {
|
||||
LWS_JWK_ENCTYPE_NONE,
|
||||
LWS_JWK_ENCTYPE_RSASSA,
|
||||
LWS_JWK_ENCTYPE_EC
|
||||
};
|
||||
|
||||
struct cb_hdr_s {
|
||||
enum lws_genhash_types hash_type;
|
||||
enum lws_genhmac_types hmac_type;
|
||||
char alg[24]; /* for jwe, the JWA enc alg name, eg "ECDH-ES" */
|
||||
char curve[16];
|
||||
enum lws_jws_algtype algtype; /* for jws, the signing cipher */
|
||||
|
||||
char is_jwe;
|
||||
};
|
22
include/libwebsockets/lws-jwe.h
Normal file
22
include/libwebsockets/lws-jwe.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Encryption
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* 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
|
||||
*
|
||||
* included from libwebsockets.h
|
||||
*/
|
|
@ -25,35 +25,113 @@
|
|||
/*! \defgroup jwk JSON Web Keys
|
||||
* ## JSON Web Keys API
|
||||
*
|
||||
* Lws provides an API to parse JSON Web Keys into a struct lws_genrsa_elements.
|
||||
* Lws provides an API to parse JSON Web Keys into a struct lws_jwk_elements.
|
||||
*
|
||||
* "oct" and "RSA" type keys are supported. For "oct" keys, they are held in
|
||||
* the "e" member of the struct lws_genrsa_elements.
|
||||
* the "e" member of the struct lws_jwk_elements.
|
||||
*
|
||||
* Keys elements are allocated on the heap. You must destroy the allocations
|
||||
* in the struct lws_genrsa_elements by calling
|
||||
* in the struct lws_jwk_elements by calling
|
||||
* lws_jwk_destroy_genrsa_elements() when you are finished with it.
|
||||
*/
|
||||
///@{
|
||||
|
||||
struct lws_jwk {
|
||||
char keytype[5]; /**< "oct" or "RSA" */
|
||||
struct lws_genrsa_elements el; /**< OCTet key is in el.e */
|
||||
enum lws_jwk_kyt {
|
||||
LWS_JWK_KYT_UNKNOWN,
|
||||
LWS_JWK_KYT_OCT,
|
||||
LWS_JWK_KYT_RSA,
|
||||
LWS_JWK_KYT_EC
|
||||
};
|
||||
|
||||
/*
|
||||
* Keytypes where the same element name is reused must all agree to put the
|
||||
* same-named element at the same e[] index. It's because we may not know the
|
||||
* keytype until the end.
|
||||
*/
|
||||
|
||||
enum enum_jwk_oct_tok {
|
||||
JWK_OCT_KEYEL_K,
|
||||
|
||||
LWS_COUNT_OCT_KEY_ELEMENTS
|
||||
};
|
||||
|
||||
enum enum_jwk_rsa_tok {
|
||||
JWK_RSA_KEYEL_E,
|
||||
JWK_RSA_KEYEL_N,
|
||||
JWK_RSA_KEYEL_D, /* note... same offset as EC D */
|
||||
JWK_RSA_KEYEL_P,
|
||||
JWK_RSA_KEYEL_Q,
|
||||
JWK_RSA_KEYEL_DP,
|
||||
JWK_RSA_KEYEL_DQ,
|
||||
JWK_RSA_KEYEL_QI,
|
||||
|
||||
LWS_COUNT_RSA_KEY_ELEMENTS
|
||||
};
|
||||
|
||||
enum enum_jwk_ec_tok {
|
||||
JWK_EC_KEYEL_CRV,
|
||||
JWK_EC_KEYEL_X,
|
||||
JWK_EC_KEYEL_D, /* note... same offset as RSA D */
|
||||
JWK_EC_KEYEL_Y,
|
||||
|
||||
LWS_COUNT_EC_KEY_ELEMENTS
|
||||
};
|
||||
|
||||
enum enum_jwk_meta_tok {
|
||||
JWK_META_KTY,
|
||||
JWK_META_KID,
|
||||
JWK_META_USE,
|
||||
JWK_META_KEY_OPS,
|
||||
JWK_META_X5C,
|
||||
JWK_META_ALG,
|
||||
|
||||
LWS_COUNT_JWK_ELEMENTS
|
||||
};
|
||||
|
||||
/* largest number of key elements for any algorithm */
|
||||
#define LWS_COUNT_ALG_KEY_ELEMENTS_MAX LWS_COUNT_RSA_KEY_ELEMENTS
|
||||
|
||||
struct lws_jwk_elements {
|
||||
uint8_t *buf;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
struct lws_jwk {
|
||||
/* key data elements */
|
||||
struct lws_jwk_elements e[LWS_COUNT_ALG_KEY_ELEMENTS_MAX];
|
||||
/* generic meta key elements, like KID */
|
||||
struct lws_jwk_elements meta[LWS_COUNT_JWK_ELEMENTS];
|
||||
int kty; /**< one of LWS_JWK_ */
|
||||
};
|
||||
|
||||
typedef int (*lws_jwk_key_import_callback)(struct lws_jwk *s, void *user);
|
||||
|
||||
/** lws_jwk_import() - Create a JSON Web key from the textual representation
|
||||
*
|
||||
* \param s: 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.
|
||||
* NULL if importing a single key with no parent "keys" JSON
|
||||
* \param in: a single JWK JSON stanza in utf-8
|
||||
* \param len: the length of the JWK JSON stanza in bytes
|
||||
*
|
||||
* Creates an lws_jwk struct filled with data from the JSON representation.
|
||||
* "oct" and "rsa" key types are supported.
|
||||
*
|
||||
* For "oct" type keys, it is loaded into el.e.
|
||||
* There are two ways to use this... with some protocols a single jwk is
|
||||
* delivered with no parent "keys": [] array. If you call this with cb and
|
||||
* user as NULL, then the input will be interpreted like that and the results
|
||||
* placed in s.
|
||||
*
|
||||
* The second case is that you are dealing with a "keys":[] array with one or
|
||||
* more keys in it. In this case, the function iterates through the keys using
|
||||
* s as a temporary jwk, and calls the user-provided callback for each key in
|
||||
* turn while it return 0 (nonzero return from the callback terminates the
|
||||
* iteration through any further keys).
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_jwk_import(struct lws_jwk *s, const char *in, size_t len);
|
||||
lws_jwk_import(struct lws_jwk *s, lws_jwk_key_import_callback cb, void *user,
|
||||
const char *in, size_t len);
|
||||
|
||||
/** lws_jwk_destroy() - Destroy a JSON Web key
|
||||
*
|
||||
|
@ -84,9 +162,21 @@ lws_jwk_export(struct lws_jwk *s, int _private, char *p, size_t len);
|
|||
* \param filename: filename to load from
|
||||
*
|
||||
* Returns 0 for OK or -1 for failure
|
||||
*
|
||||
* There are two ways to use this... with some protocols a single jwk is
|
||||
* delivered with no parent "keys": [] array. If you call this with cb and
|
||||
* user as NULL, then the input will be interpreted like that and the results
|
||||
* placed in s.
|
||||
*
|
||||
* The second case is that you are dealing with a "keys":[] array with one or
|
||||
* more keys in it. In this case, the function iterates through the keys using
|
||||
* s as a temporary jwk, and calls the user-provided callback for each key in
|
||||
* turn while it return 0 (nonzero return from the callback terminates the
|
||||
* iteration through any further keys, leaving the last one in s).
|
||||
*/
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_load(struct lws_jwk *s, const char *filename);
|
||||
lws_jwk_load(struct lws_jwk *s, const char *filename,
|
||||
lws_jwk_key_import_callback cb, void *user);
|
||||
|
||||
/** lws_jwk_save() - Export a JSON Web key to a file
|
||||
*
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
///@{
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk);
|
||||
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
|
||||
struct lws_context *context);
|
||||
|
||||
/**
|
||||
* lws_jws_sign_from_b64() - add b64 sig to b64 hdr + payload
|
||||
|
@ -47,6 +48,7 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk);
|
|||
* \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)
|
||||
*
|
||||
* 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,7 +64,8 @@ lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk);
|
|||
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,
|
||||
enum lws_genhash_types hash_type, struct lws_jwk *jwk);
|
||||
enum lws_genhash_types hash_type, struct lws_jwk *jwk,
|
||||
struct lws_context *context);
|
||||
|
||||
/**
|
||||
* lws_jws_create_packet() - add b64 sig to b64 hdr + payload
|
||||
|
@ -73,6 +76,7 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
|
|||
* \param nonce: Nonse string to include in protected header
|
||||
* \param out: buffer to take signed packet
|
||||
* \param out_len: size of \p out buffer
|
||||
* \param conext: lws_context to get random from
|
||||
*
|
||||
* This creates a "flattened" JWS packet from the jwk and the plaintext
|
||||
* payload, and signs it. The packet is written into \p out.
|
||||
|
@ -84,7 +88,8 @@ lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
|
|||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
|
||||
const char *nonce, char *out, size_t out_len);
|
||||
const char *nonce, char *out, size_t out_len,
|
||||
struct lws_context *context);
|
||||
|
||||
/**
|
||||
* lws_jws_base64_enc() - encode input data into b64url data
|
||||
|
@ -98,4 +103,21 @@ lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
|
|||
*/
|
||||
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
|
||||
*
|
||||
* \param in: the incoming plaintext
|
||||
* \param in_len: the length of the incoming plaintext in bytes
|
||||
* \param first: nonzero if the first section
|
||||
* \param out: the buffer to store the b64url encoded data to
|
||||
* \param out_max: the length of \p out in bytes
|
||||
*
|
||||
* Returns either -1 if problems, or the number of bytes written to \p out.
|
||||
* If the section is not the first one, '.' is prepended.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
|
||||
char *end);
|
||||
///@}
|
||||
|
|
|
@ -6,6 +6,7 @@ Path|Sources
|
|||
---|---
|
||||
lib/core|Core lws code related to generic fd and wsi servicing and management
|
||||
lib/event-libs|Code containing optional event-lib specific adaptations
|
||||
lib/jose|JOSE / JWS / JWK / JWE implementations
|
||||
lib/misc|Code for various mostly optional miscellaneous features
|
||||
lib/plat|Platform-specific adaptation code
|
||||
lib/roles|Code for specific optional wsi roles, eg, http/1, h2, ws, raw, etc
|
||||
|
|
|
@ -1273,10 +1273,6 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
/* expedite post-context init (eg, protocols) */
|
||||
lws_cancel_service(context);
|
||||
|
||||
#if defined(LWS_WITH_SELFTESTS)
|
||||
lws_jws_selftest();
|
||||
#endif
|
||||
|
||||
return context;
|
||||
|
||||
bail:
|
||||
|
|
|
@ -1337,7 +1337,7 @@ int
|
|||
lws_tls_check_cert_lifetime(struct lws_vhost *vhost);
|
||||
|
||||
int lws_jws_selftest(void);
|
||||
|
||||
int lws_jwe_selftest(void);
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
LWS_EXTERN int lws_client_socket_service(struct lws *wsi,
|
||||
|
|
51
lib/jose/README.md
Normal file
51
lib/jose/README.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
# JOSE support
|
||||
|
||||
JOSE is a set of web standards aimed at encapsulating crypto
|
||||
operations flexibly inside JSON objects.
|
||||
|
||||
Lws provides lightweight apis to performs operations on JWK, JWS and JWE
|
||||
independent of the tls backend in use. The JSON parsing is handled by the lws
|
||||
lejp stream parser.
|
||||
|
||||
|Part|RFC|Function|
|
||||
|---|---|---|
|
||||
|JWS|[RFC7515](https://tools.ietf.org/html/rfc7515)|JSON Web Signatures|
|
||||
|JWE|[RFC7516](https://tools.ietf.org/html/rfc7516)|JSON Web Encryption|
|
||||
|JWK|[RFC7517](https://tools.ietf.org/html/rfc7517)|JSON Web Keys|
|
||||
|JWA|[RFC7518](https://tools.ietf.org/html/rfc7518)|JSON Web Algorithms|
|
||||
|
||||
JWA is a set of recommendations for which combinations of algorithms
|
||||
are deemed desirable and secure, which implies what must be done for
|
||||
useful implementations of JWS, JWE and JWK.
|
||||
|
||||
## Supported algorithms
|
||||
|
||||
Symmetric ciphers are not currently supported... symmetric keys and HMAC
|
||||
are supported though.
|
||||
|
||||
For the required and recommended asymmetric algorithms, support currently
|
||||
looks like this
|
||||
|
||||
|JWK kty|JWA|lws|
|
||||
|---|---|---|
|
||||
|EC|Recommended+|no|
|
||||
|RSA|Required|yes|
|
||||
|oct|Required|yes|
|
||||
|
||||
|JWE alg|JWA|lws|
|
||||
|---|---|---|
|
||||
|RSA1_5|Recommended-|yes (no JWE yet but lws_genrsa supports)|
|
||||
|RSA-OAEP|Recommended+|no|
|
||||
|ECDH-ES|Recommended+|no|
|
||||
|
||||
|JWS alg|JWA|lws|
|
||||
|---|---|---|
|
||||
|HS256|Required|yes|
|
||||
|RS256|Recommended+|yes|
|
||||
|ES256|Recommended|no|
|
||||
|
||||
## API tests
|
||||
|
||||
See `./minimal-examples/api-tests/api-test-jose/` for example test code.
|
||||
The tests are built and confirmed during CI.
|
||||
|
27
lib/jose/jwe/jwe.c
Normal file
27
lib/jose/jwe/jwe.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
*
|
||||
* This supports RFC7516 JSON Web Encryption
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "core/private.h"
|
||||
|
497
lib/jose/jwk/jwk.c
Normal file
497
lib/jose/jwk/jwk.c
Normal file
|
@ -0,0 +1,497 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Key support
|
||||
*
|
||||
* Copyright (C) 2017 - 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
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char * const kyt_names[] = {
|
||||
"unknown", /* LWS_JWK_KYT_UNKNOWN */
|
||||
"oct", /* LWS_JWK_KYT_OCT */
|
||||
"RSA", /* LWS_JWK_KYT_RSA */
|
||||
"EC" /* LWS_JWK_KYT_EC */
|
||||
};
|
||||
|
||||
/*
|
||||
* These are the entire legal token set for names in jwk.
|
||||
*
|
||||
* The first version is used to parse a detached single jwk that don't have any
|
||||
* parent JSON context. The second version is used to parse full jwk objects
|
||||
* that has a "keys": [ ] array containing the keys.
|
||||
*/
|
||||
|
||||
static const char * const jwk_tok[] = {
|
||||
"keys[]", /* dummy */
|
||||
"e", "n", "d", "p", "q", "dp", "dq", "qi", /* RSA */
|
||||
"kty", /* generic */
|
||||
"k", /* symmetric oct key data */
|
||||
"crv", "x", "y", /* EC (also "D") */
|
||||
"kid", /* generic */
|
||||
"use" /* mutually exclusive with "key_ops" */,
|
||||
"key_ops" /* mutually exclusive with "use" */,
|
||||
"x5c", /* generic */
|
||||
"alg" /* generic */
|
||||
}, * const jwk_outer_tok[] = {
|
||||
"keys[]",
|
||||
"keys[].e", "keys[].n", "keys[].d", "keys[].p", "keys[].q", "keys[].dp",
|
||||
"keys[].dq", "keys[].qi",
|
||||
|
||||
"keys[].kty", "keys[].k", /* generic */
|
||||
"keys[].crv", "keys[].x", "keys[].y", /* EC (also "D") */
|
||||
"keys[].kid", "keys[].use" /* mutually exclusive with "key_ops" */,
|
||||
"keys[].key_ops", /* mutually exclusive with "use" */
|
||||
"keys[].x5c", "keys[].alg"
|
||||
};
|
||||
|
||||
/* information about each token declared above */
|
||||
|
||||
#define FLAG_META (1 << 12)
|
||||
#define FLAG_RSA (1 << 13)
|
||||
#define FLAG_EC (1 << 14)
|
||||
#define FLAG_OCT (1 << 15)
|
||||
|
||||
unsigned short tok_map[] = {
|
||||
FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | 0, /* padding */
|
||||
FLAG_RSA | JWK_RSA_KEYEL_E,
|
||||
FLAG_RSA | JWK_RSA_KEYEL_N,
|
||||
FLAG_RSA | FLAG_EC | JWK_RSA_KEYEL_D,
|
||||
FLAG_RSA | JWK_RSA_KEYEL_P,
|
||||
FLAG_RSA | JWK_RSA_KEYEL_Q,
|
||||
FLAG_RSA | JWK_RSA_KEYEL_DP,
|
||||
FLAG_RSA | JWK_RSA_KEYEL_DQ,
|
||||
FLAG_RSA | JWK_RSA_KEYEL_QI,
|
||||
|
||||
FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KTY,
|
||||
FLAG_OCT | JWK_OCT_KEYEL_K,
|
||||
|
||||
FLAG_EC | JWK_EC_KEYEL_CRV,
|
||||
FLAG_EC | JWK_EC_KEYEL_X,
|
||||
FLAG_EC | JWK_EC_KEYEL_Y,
|
||||
|
||||
FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KID,
|
||||
FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_USE,
|
||||
|
||||
FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KEY_OPS,
|
||||
FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_X5C,
|
||||
FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_ALG,
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
static int
|
||||
_lws_jwk_set_element_jwk(struct lws_jwk_elements *e, char *in, int len)
|
||||
{
|
||||
e->buf = lws_malloc(len + 1, "jwk");
|
||||
if (!e->buf)
|
||||
return -1;
|
||||
|
||||
memcpy(e->buf, in, len);
|
||||
e->buf[len] = '\0';
|
||||
e->len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_lws_jwk_set_element_jwk_b64(struct lws_jwk_elements *e, char *in, int len)
|
||||
{
|
||||
int dec_size = ((len * 3) / 4) + 4, n;
|
||||
|
||||
e->buf = lws_malloc(dec_size, "jwk");
|
||||
if (!e->buf)
|
||||
return -1;
|
||||
|
||||
n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
e->len = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_jwk_destroy_elements(struct lws_jwk_elements *el, int m)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < m; n++)
|
||||
if (el[n].buf)
|
||||
lws_free_set_NULL(el[n].buf);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_jwk_destroy(struct lws_jwk *s)
|
||||
{
|
||||
lws_jwk_destroy_elements(s->e, LWS_ARRAY_SIZE(s->e));
|
||||
lws_jwk_destroy_elements(s->meta, LWS_ARRAY_SIZE(s->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;
|
||||
int idx, poss;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_START)
|
||||
cbs->pos = 0;
|
||||
|
||||
if (reason == LEJPCB_OBJECT_START && ctx->path_match == 0 + 1)
|
||||
/*
|
||||
* new keys[] member is starting
|
||||
*
|
||||
* Until we see some JSON names, it could be anything...
|
||||
* there is no requirement for kty to be given first and eg,
|
||||
* ACME specifies the keys must be ordered in lexographic
|
||||
* order - where kty is not first.
|
||||
*/
|
||||
cbs->possible = FLAG_RSA | FLAG_EC | FLAG_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)) {
|
||||
|
||||
lwsl_notice("%s: user cb halts import\n", __func__);
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* clear it down */
|
||||
lws_jwk_destroy(cbs->s);
|
||||
cbs->possible = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
if (ctx->path_match == 0 + 1)
|
||||
return 0;
|
||||
|
||||
idx = tok_map[ctx->path_match - 1];
|
||||
|
||||
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
|
||||
* kty told. */
|
||||
case FLAG_RSA | FLAG_EC | FLAG_OCT | FLAG_META | JWK_META_KTY:
|
||||
|
||||
if (!strcmp(ctx->buf, "oct")) {
|
||||
if (!(cbs->possible & FLAG_OCT))
|
||||
goto elements_mismatch;
|
||||
s->kty = LWS_JWK_KYT_OCT;
|
||||
cbs->possible = FLAG_OCT;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RSA")) {
|
||||
if (!(cbs->possible & FLAG_RSA))
|
||||
goto elements_mismatch;
|
||||
s->kty = LWS_JWK_KYT_RSA;
|
||||
cbs->possible = FLAG_RSA;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "EC")) {
|
||||
if (!(cbs->possible & FLAG_EC))
|
||||
goto elements_mismatch;
|
||||
s->kty = LWS_JWK_KYT_EC;
|
||||
cbs->possible = FLAG_EC;
|
||||
break;
|
||||
}
|
||||
lwsl_err("%s: Unknown KTY '%s'\n", __func__, ctx->buf);
|
||||
return -1;
|
||||
|
||||
default:
|
||||
|
||||
if (cbs->pos + ctx->npos >= cbs->b64max)
|
||||
goto bail;
|
||||
|
||||
memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
|
||||
cbs->pos += ctx->npos;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_CHUNK)
|
||||
return 0;
|
||||
|
||||
/* chunking has been collated */
|
||||
|
||||
poss = idx & (FLAG_RSA | FLAG_EC | FLAG_OCT);
|
||||
cbs->possible &= poss;
|
||||
if (!cbs->possible)
|
||||
goto elements_mismatch;
|
||||
|
||||
if (idx & FLAG_META) {
|
||||
if (_lws_jwk_set_element_jwk(&s->meta[idx & 0x7f],
|
||||
cbs->b64, cbs->pos) < 0)
|
||||
goto bail;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* key data... do the base64 decode then */
|
||||
|
||||
if (_lws_jwk_set_element_jwk_b64(&s->e[idx & 0x7f],
|
||||
cbs->b64, cbs->pos) < 0)
|
||||
goto bail;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
elements_mismatch:
|
||||
lwsl_err("%s: jwk elements mismatch\n", __func__);
|
||||
|
||||
bail:
|
||||
lwsl_err("%s: element failed\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_import(struct lws_jwk *s, lws_jwk_key_import_callback cb, void *user,
|
||||
const char *in, size_t len)
|
||||
{
|
||||
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;
|
||||
|
||||
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 = FLAG_RSA | FLAG_EC | FLAG_OCT;
|
||||
|
||||
if (cb == NULL)
|
||||
tok = 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);
|
||||
|
||||
if (m < 0) {
|
||||
lwsl_notice("%s: parse got %d\n", __func__, m);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->kty == LWS_JWK_KYT_UNKNOWN) {
|
||||
lwsl_notice("%s: missing or unknown kyt\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
|
||||
{
|
||||
char *start = p, *end = &p[len - 1];
|
||||
int n, limit = LWS_COUNT_JWK_ELEMENTS;
|
||||
|
||||
/* RFC7638 lexicographic order requires
|
||||
* RSA: e -> kty -> n
|
||||
* oct: k -> kty
|
||||
*/
|
||||
|
||||
p += lws_snprintf(p, end - p, "{");
|
||||
|
||||
switch (s->kty) {
|
||||
|
||||
case LWS_JWK_KYT_OCT:
|
||||
if (!s->e[JWK_OCT_KEYEL_K].buf)
|
||||
return -1;
|
||||
|
||||
p += lws_snprintf(p, end - p, "\"k\":\"");
|
||||
n = lws_jws_base64_enc((const char *)s->e[JWK_OCT_KEYEL_K].buf,
|
||||
s->e[JWK_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_JWK_KYT_RSA:
|
||||
if (!s->e[JWK_RSA_KEYEL_E].buf ||
|
||||
!s->e[JWK_RSA_KEYEL_N].buf ||
|
||||
(private && (!s->e[JWK_RSA_KEYEL_D].buf ||
|
||||
!s->e[JWK_RSA_KEYEL_P].buf ||
|
||||
!s->e[JWK_RSA_KEYEL_Q].buf))
|
||||
) {
|
||||
lwsl_notice("%s: not enough elements filled\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!private)
|
||||
limit = JWK_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_JWK_KYT_EC:
|
||||
return p - start;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
lwsl_err("%s: unknown key type %d\n", __func__, s->kty);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
|
||||
{
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
int tmpsize = 2536, n;
|
||||
char *tmp;
|
||||
|
||||
tmp = lws_malloc(tmpsize, "rfc7638 tmp");
|
||||
|
||||
n = lws_jwk_export(s, 0, tmp, tmpsize);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
|
||||
goto bail;
|
||||
|
||||
if (lws_genhash_update(&hash_ctx, tmp, n)) {
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
lws_free(tmp);
|
||||
|
||||
if (lws_genhash_destroy(&hash_ctx, digest32))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(tmp);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_load(struct lws_jwk *s, const char *filename,
|
||||
lws_jwk_key_import_callback cb, void *user)
|
||||
{
|
||||
int buflen = 4096;
|
||||
char *buf = lws_malloc(buflen, "jwk-load");
|
||||
int n;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_plat_read_file(filename, buf, buflen);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
n = lws_jwk_import(s, cb, user, buf, n);
|
||||
lws_free(buf);
|
||||
|
||||
return n;
|
||||
bail:
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_save(struct lws_jwk *s, const char *filename)
|
||||
{
|
||||
int buflen = 4096;
|
||||
char *buf = lws_malloc(buflen, "jwk-save");
|
||||
int n, m;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_jwk_export(s, 1, buf, buflen);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
m = lws_plat_write_file(filename, buf, n);
|
||||
|
||||
lws_free(buf);
|
||||
if (m)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
254
lib/jose/jws/jose.c
Normal file
254
lib/jose/jws/jose.c
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Signature support
|
||||
*
|
||||
* Copyright (C) 2017 - 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
|
||||
*
|
||||
* JOSE is actually specified as part of JWS RFC7515. JWE references RFC7515
|
||||
* to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c.
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const char * const jws_jose[] = {
|
||||
"alg", /* REQUIRED */
|
||||
"jku",
|
||||
"jwk",
|
||||
"kid",
|
||||
"x5u",
|
||||
"x5c",
|
||||
"x5t",
|
||||
"x5t#S256",
|
||||
"typ",
|
||||
"cty",
|
||||
"crit",
|
||||
|
||||
/* valid for JWE only below here */
|
||||
|
||||
"enc",
|
||||
"zip", /* ("DEF" = deflate) */
|
||||
|
||||
"epk", /* valid for JWE ECDH only */
|
||||
"apu", /* valid for JWE ECDH only */
|
||||
"apv", /* valid for JWE ECDH only */
|
||||
"iv", /* valid for JWE AES only */
|
||||
"tag", /* valid for JWE AES only */
|
||||
"p2s", /* valid for JWE PBES2 only */
|
||||
"p2c" /* valid for JWE PBES2 only */
|
||||
};
|
||||
|
||||
static signed char
|
||||
lws_jws_jose_cb(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct cb_hdr_s *s = (struct cb_hdr_s *)ctx->user;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
|
||||
/* strings */
|
||||
|
||||
case LJJHI_ALG: /* REQUIRED */
|
||||
|
||||
lws_strncpy(s->alg, ctx->buf, sizeof(s->alg));
|
||||
|
||||
if (s->is_jwe) {
|
||||
|
||||
lwsl_err("%s: JWE alg\n", __func__);
|
||||
|
||||
/* interpret as for JWE... just store the string */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* interpret as for JWS */
|
||||
|
||||
if (!strcmp(ctx->buf, "HS256")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA256;
|
||||
s->algtype = LWS_JWK_ENCTYPE_NONE;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "HS384")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA384;
|
||||
s->algtype = LWS_JWK_ENCTYPE_NONE;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "HS512")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA512;
|
||||
s->algtype = LWS_JWK_ENCTYPE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(ctx->buf, "RS256")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA256;
|
||||
s->algtype = LWS_JWK_ENCTYPE_RSASSA;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS384")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA384;
|
||||
s->algtype = LWS_JWK_ENCTYPE_RSASSA;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS512")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA512;
|
||||
s->algtype = LWS_JWK_ENCTYPE_RSASSA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(ctx->buf, "ES256")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA256;
|
||||
s->algtype = LWS_JWK_ENCTYPE_EC;
|
||||
strncpy(s->curve, "P-256", sizeof(s->curve));
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "ES384")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA384;
|
||||
s->algtype = LWS_JWK_ENCTYPE_EC;
|
||||
strncpy(s->curve, "P-384", sizeof(s->curve));
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "ES512")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA512;
|
||||
s->algtype = LWS_JWK_ENCTYPE_EC;
|
||||
strncpy(s->curve, "P-521", sizeof(s->curve));
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
case LJJHI_TYP: /* Optional: string: media type */
|
||||
if (strcmp(ctx->buf, "JWT"))
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_JKU: /* Optional: string */
|
||||
case LJJHI_KID: /* Optional: string */
|
||||
case LJJHI_X5U: /* Optional: string: url of public key cert / chain */
|
||||
case LJJHI_CTY: /* Optional: string: content media type */
|
||||
|
||||
/* base64 */
|
||||
|
||||
case LJJHI_X5C: /* Optional: base64 (NOT -url): actual cert */
|
||||
|
||||
/* base64-url */
|
||||
|
||||
case LJJHI_X5T: /* Optional: base64url: SHA-1 of actual cert */
|
||||
case LJJHI_X5T_S256: /* Optional: base64url: SHA-256 of actual cert */
|
||||
|
||||
/* array of strings */
|
||||
|
||||
case LJJHI_CRIT: /* Optional for send, REQUIRED: array of strings:
|
||||
* mustn't contain standardized strings or null set */
|
||||
break;
|
||||
|
||||
/* jwk child */
|
||||
|
||||
case LJJHI_JWK: /* Optional: jwk JSON object: public key: */
|
||||
|
||||
/* past here, JWE only */
|
||||
|
||||
case LJJHI_ENC: /* JWE only: Optional: string */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_ZIP: /* JWE only: Optional: string ("DEF" = deflate) */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_EPK: /* Additional arg for JWE ECDH */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_APU: /* Additional arg for JWE ECDH */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_APV: /* Additional arg for JWE ECDH */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_IV: /* Additional arg for JWE AES */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_TAG: /* Additional arg for JWE AES */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LJJHI_P2S: /* Additional arg for JWE PBES2 */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
case LJJHI_P2C: /* Additional arg for JWE PBES2 */
|
||||
if (!s->is_jwe)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
/* ignore what we don't understand */
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_jose_parse(struct cb_hdr_s *args, uint8_t *buf, int n, int is_jwe)
|
||||
{
|
||||
struct lejp_ctx jctx;
|
||||
int m;
|
||||
|
||||
args->alg[0] = '\0';
|
||||
args->curve[0] = '\0';
|
||||
args->algtype = -1;
|
||||
args->is_jwe = is_jwe;
|
||||
|
||||
lejp_construct(&jctx, lws_jws_jose_cb, args, jws_jose,
|
||||
LWS_ARRAY_SIZE(jws_jose));
|
||||
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
|
||||
lejp_destruct(&jctx);
|
||||
if (m < 0) {
|
||||
lwsl_notice("parse got %d: alg %s\n", m, args->alg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_jws_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n)
|
||||
{
|
||||
return lws_jose_parse(args, buf, n, 0);
|
||||
}
|
||||
|
||||
int
|
||||
lws_jwe_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n)
|
||||
{
|
||||
return lws_jose_parse(args, buf, n, 1);
|
||||
}
|
367
lib/jose/jws/jws.c
Normal file
367
lib/jose/jws/jws.c
Normal file
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Signature support
|
||||
*
|
||||
* Copyright (C) 2017 - 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
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
#include "private.h"
|
||||
|
||||
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)
|
||||
return n; /* too large for output buffer */
|
||||
|
||||
/* trim the terminal = */
|
||||
while (n && out[n - 1] == '=')
|
||||
n--;
|
||||
|
||||
out[n] = '\0';
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
|
||||
char *end)
|
||||
{
|
||||
int n, len = (end - *p) - 1;
|
||||
char *p_entry = *p;
|
||||
|
||||
if (len < 3)
|
||||
return -1;
|
||||
|
||||
if (!first)
|
||||
*(*p)++ = '.';
|
||||
|
||||
n = lws_jws_base64_enc(in, in_len, *p, len - 1);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
*p += n;
|
||||
|
||||
return (*p) - p_entry;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_jws_find_sig(const char *in, size_t len)
|
||||
{
|
||||
const char *p = in + len - 1;
|
||||
|
||||
while (len--)
|
||||
if (*p == '.')
|
||||
return (p + 1) - in;
|
||||
else
|
||||
p--;
|
||||
|
||||
lwsl_notice("%s failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk,
|
||||
struct lws_context *context)
|
||||
{
|
||||
int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
struct lws_genrsa_ctx rsactx;
|
||||
struct lws_genhmac_ctx ctx;
|
||||
struct cb_hdr_s args;
|
||||
char buf[2048];
|
||||
|
||||
/* 1) there has to be a signature */
|
||||
|
||||
if (sig_pos < 0)
|
||||
return -1;
|
||||
|
||||
/* 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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 5) decode the B64URL signature part into buf / m */
|
||||
|
||||
m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
|
||||
buf, sizeof(buf) - 1);
|
||||
|
||||
switch (args.algtype) {
|
||||
case LWS_JWK_ENCTYPE_RSASSA:
|
||||
|
||||
/* RSASSA-PKCS1-v1_5 using SHA-256/384/512 */
|
||||
|
||||
/* 6(RSA): compute the hash of the payload into "digest" */
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, args.hash_type))
|
||||
return -1;
|
||||
|
||||
if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
|
||||
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);
|
||||
|
||||
if (lws_genrsa_create(&rsactx, jwk->e, context)) {
|
||||
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = lws_genrsa_public_verify(&rsactx, digest, args.hash_type,
|
||||
(uint8_t *)buf, m);
|
||||
|
||||
lws_genrsa_destroy(&rsactx);
|
||||
if (n < 0) {
|
||||
lwsl_notice("decrypt fail\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LWS_JWK_ENCTYPE_NONE:
|
||||
|
||||
/* SHA256/384/512 HMAC */
|
||||
|
||||
h_len = lws_genhmac_size(args.hmac_type);
|
||||
if (m < 0 || m != h_len)
|
||||
return -1;
|
||||
|
||||
/* 6) compute HMAC over payload */
|
||||
|
||||
if (lws_genhmac_init(&ctx, args.hmac_type,
|
||||
jwk->e[JWK_RSA_KEYEL_E].buf,
|
||||
jwk->e[JWK_RSA_KEYEL_E].len))
|
||||
return -1;
|
||||
|
||||
if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
|
||||
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)) {
|
||||
lwsl_notice("digest mismatch\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LWS_JWK_ENCTYPE_EC:
|
||||
|
||||
lwsl_err("%s: EC not supported yet\n", __func__);
|
||||
return -1;
|
||||
|
||||
default:
|
||||
lwsl_err("%s: unknown alg from jose\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
enum lws_genhash_types hash_type, struct lws_jwk *jwk,
|
||||
struct lws_context *context)
|
||||
{
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
struct lws_genrsa_ctx rsactx;
|
||||
uint8_t *buf;
|
||||
int n;
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, 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;
|
||||
|
||||
if (jwk->kty == LWS_JWK_KYT_RSA) {
|
||||
if (lws_genrsa_create(&rsactx, jwk->e, context)) {
|
||||
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = jwk->e[JWK_RSA_KEYEL_N].len;
|
||||
buf = lws_malloc(n, "jws sign");
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_genrsa_public_sign(&rsactx, digest, hash_type, buf, n);
|
||||
lws_genrsa_destroy(&rsactx);
|
||||
if (n < 0) {
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
|
||||
lws_free(buf);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
if (jwk->kty == LWS_JWK_KYT_OCT)
|
||||
return lws_jws_base64_enc((char *)digest,
|
||||
lws_genhash_size(hash_type),
|
||||
b64_sig, sig_len);
|
||||
|
||||
/* unknown key type */
|
||||
|
||||
return -1;
|
||||
|
||||
hash_fail:
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_create_packet(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;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
n = LWS_PRE + 2048;
|
||||
buf = malloc(n);
|
||||
if (!buf) {
|
||||
lwsl_notice("%s: malloc %d failed\n", __func__, n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = start = buf + LWS_PRE;
|
||||
end = buf + n - LWS_PRE - 1;
|
||||
|
||||
/*
|
||||
* temporary JWS protected header plaintext
|
||||
*/
|
||||
|
||||
p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
|
||||
n = lws_jwk_export(jwk, 0, p, end - p);
|
||||
if (n < 0) {
|
||||
lwsl_notice("failed to export jwk\n");
|
||||
|
||||
goto bail;
|
||||
}
|
||||
p += n;
|
||||
p += lws_snprintf(p, end - p, ",\"nonce\":\"%s\"}", nonce);
|
||||
|
||||
/*
|
||||
* prepare the signed outer JSON with all the parts in
|
||||
*/
|
||||
|
||||
p1 = out;
|
||||
end1 = out + out_len - 1;
|
||||
|
||||
p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\"");
|
||||
b64_hdr = 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;
|
||||
p1 += n;
|
||||
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
|
||||
b64_pay = 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;
|
||||
|
||||
p1 += n;
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\"");
|
||||
|
||||
/*
|
||||
* 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, LWS_GENHASH_TYPE_SHA256, jwk,
|
||||
context);
|
||||
if (n < 0) {
|
||||
lwsl_notice("sig gen failed\n");
|
||||
|
||||
goto bail;
|
||||
}
|
||||
p1 += n;
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\"}");
|
||||
|
||||
free(buf);
|
||||
|
||||
return p1 - out;
|
||||
|
||||
bail:
|
||||
free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
29
lib/jose/jws/private.h
Normal file
29
lib/jose/jws/private.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Signature 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
|
||||
*
|
||||
* JOSE is actually specified as part of JWS RFC7515. JWE references RFC7515
|
||||
* to specify its JOSE JSON object. So it lives in ./lib/jose/jws/jose.c.
|
||||
*/
|
||||
|
||||
int
|
||||
lws_jws_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n);
|
||||
|
||||
int
|
||||
lws_jwe_parse_jose(struct cb_hdr_s *args, uint8_t *buf, int n);
|
23
lib/jose/private.h
Normal file
23
lib/jose/private.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* libwebsockets - jose private header
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
void
|
||||
lws_jwk_destroy_elements(struct lws_jwk_elements *el, int m);
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Key support
|
||||
*
|
||||
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char * const jwk_tok[] = {
|
||||
"e", "n", "d", "p", "q", "dp", "dq", "qi", "kty", "k",
|
||||
};
|
||||
|
||||
static int
|
||||
_lws_jwk_set_element(struct lws_genrsa_element *e, char *in, int len)
|
||||
{
|
||||
int dec_size = ((len * 3) / 4) + 4, n;
|
||||
|
||||
e->buf = lws_malloc(dec_size, "jwk");
|
||||
if (!e->buf)
|
||||
return -1;
|
||||
|
||||
n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
e->len = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cb_lws_jwk {
|
||||
struct lws_jwk *s;
|
||||
char *b64;
|
||||
int b64max;
|
||||
int pos;
|
||||
};
|
||||
|
||||
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;
|
||||
int idx;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_START)
|
||||
cbs->pos = 0;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
case JWK_KTY:
|
||||
lws_strncpy(s->keytype, ctx->buf, sizeof(s->keytype));
|
||||
if (!strcmp(ctx->buf, "oct")) {
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RSA")) {
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
|
||||
case JWK_KEY:
|
||||
// if (strcmp(s->keytype, "oct"))
|
||||
// return -1;
|
||||
idx = JWK_KEY_E;
|
||||
goto read_element1;
|
||||
|
||||
case JWK_KEY_N:
|
||||
case JWK_KEY_E:
|
||||
case JWK_KEY_D:
|
||||
case JWK_KEY_P:
|
||||
case JWK_KEY_Q:
|
||||
case JWK_KEY_DP:
|
||||
case JWK_KEY_DQ:
|
||||
case JWK_KEY_QI:
|
||||
idx = ctx->path_match - 1;
|
||||
goto read_element;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
read_element:
|
||||
/* kty is no longer first in lex order */
|
||||
// if (strcmp(s->keytype, "RSA"))
|
||||
// return -1;
|
||||
|
||||
read_element1:
|
||||
|
||||
if (cbs->pos + ctx->npos >= cbs->b64max)
|
||||
return -1;
|
||||
|
||||
memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
|
||||
cbs->pos += ctx->npos;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_CHUNK)
|
||||
return 0;
|
||||
|
||||
if (_lws_jwk_set_element(&s->el.e[idx], cbs->b64, cbs->pos) < 0) {
|
||||
lws_jwk_destroy_genrsa_elements(&s->el);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_import(struct lws_jwk *s, const char *in, size_t len)
|
||||
{
|
||||
struct lejp_ctx jctx;
|
||||
struct cb_lws_jwk cbs;
|
||||
const int b64max = (((8192 / 8) * 4) / 3) + 1; /* enough for 8K key */
|
||||
char b64[b64max];
|
||||
int m;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
cbs.s = s;
|
||||
cbs.b64 = b64;
|
||||
cbs.b64max = b64max;
|
||||
cbs.pos = 0;
|
||||
lejp_construct(&jctx, cb_jwk, &cbs, jwk_tok, LWS_ARRAY_SIZE(jwk_tok));
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
|
||||
lejp_destruct(&jctx);
|
||||
|
||||
if (m < 0) {
|
||||
lwsl_notice("%s: parse got %d\n", __func__, m);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_jwk_destroy(struct lws_jwk *s)
|
||||
{
|
||||
lws_jwk_destroy_genrsa_elements(&s->el);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
|
||||
{
|
||||
char *start = p, *end = &p[len - 1];
|
||||
int n, limit = LWS_COUNT_RSA_ELEMENTS;
|
||||
|
||||
/* RFC7638 lexicographic order requires
|
||||
* RSA: e -> kty -> n
|
||||
* oct: k -> kty
|
||||
*/
|
||||
|
||||
p += lws_snprintf(p, end - p, "{");
|
||||
|
||||
if (!strcmp(s->keytype, "oct")) {
|
||||
if (!s->el.e[JWK_KEY_E].buf)
|
||||
return -1;
|
||||
|
||||
p += lws_snprintf(p, end - p, "\"k\":\"");
|
||||
n = lws_jws_base64_enc((const char *)s->el.e[JWK_KEY_E].buf,
|
||||
s->el.e[JWK_KEY_E].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\"}", s->keytype);
|
||||
|
||||
return p - start;
|
||||
}
|
||||
|
||||
if (!strcmp(s->keytype, "RSA")) {
|
||||
if (!s->el.e[JWK_KEY_E].buf ||
|
||||
!s->el.e[JWK_KEY_N].buf ||
|
||||
(private && (!s->el.e[JWK_KEY_D].buf ||
|
||||
!s->el.e[JWK_KEY_P].buf ||
|
||||
!s->el.e[JWK_KEY_Q].buf))
|
||||
) {
|
||||
lwsl_notice("%s: not enough elements filled\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!private)
|
||||
limit = JWK_KEY_N + 1;
|
||||
|
||||
for (n = 0; n < limit; n++) {
|
||||
int m;
|
||||
|
||||
if (!s->el.e[n].buf)
|
||||
continue;
|
||||
lwsl_info("%d: len %d\n", n, s->el.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->el.e[n].buf,
|
||||
s->el.e[n].len, p,
|
||||
end - p - 4);
|
||||
if (m < 0) {
|
||||
lwsl_notice("%s: enc fail inlen %d outlen %d\n",
|
||||
__func__, (int)s->el.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\"",
|
||||
s->keytype);
|
||||
}
|
||||
|
||||
p += lws_snprintf(p, end - p, "}");
|
||||
|
||||
return p - start;
|
||||
}
|
||||
|
||||
lwsl_err("%s: unknown key type %s\n", __func__, s->keytype);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
|
||||
{
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
int tmpsize = 2536, n;
|
||||
char *tmp;
|
||||
|
||||
tmp = lws_malloc(tmpsize, "rfc7638 tmp");
|
||||
|
||||
n = lws_jwk_export(s, 0, tmp, tmpsize);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
|
||||
goto bail;
|
||||
|
||||
if (lws_genhash_update(&hash_ctx, tmp, n)) {
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
lws_free(tmp);
|
||||
|
||||
if (lws_genhash_destroy(&hash_ctx, digest32))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(tmp);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_load(struct lws_jwk *s, const char *filename)
|
||||
{
|
||||
int buflen = 4096;
|
||||
char *buf = lws_malloc(buflen, "jwk-load");
|
||||
int n;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_plat_read_file(filename, buf, buflen);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
n = lws_jwk_import(s, buf, n);
|
||||
lws_free(buf);
|
||||
|
||||
return n;
|
||||
bail:
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_save(struct lws_jwk *s, const char *filename)
|
||||
{
|
||||
int buflen = 4096;
|
||||
char *buf = lws_malloc(buflen, "jwk-save");
|
||||
int n, m;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_jwk_export(s, 1, buf, buflen);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
m = lws_plat_write_file(filename, buf, n);
|
||||
|
||||
lws_free(buf);
|
||||
if (m)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -1,642 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Signature support
|
||||
*
|
||||
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
/*
|
||||
* JSON Web Signature is defined in RFC7515
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc7515
|
||||
*
|
||||
* It's basically a way to wrap some JSON with a JSON "header" describing the
|
||||
* crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
|
||||
*
|
||||
* The signature stays with the content, it serves a different purpose than eg
|
||||
* a TLS tunnel to transfer it.
|
||||
*
|
||||
* RFC7518 (JSON Web Algorithms) says for the "alg" names
|
||||
*
|
||||
* | HS256 | HMAC using SHA-256 | Required |
|
||||
* | HS384 | HMAC using SHA-384 | Optional |
|
||||
* | HS512 | HMAC using SHA-512 | Optional |
|
||||
* | RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
|
||||
* | RS384 | RSASSA-PKCS1-v1_5 using | Optional |
|
||||
* | | SHA-384 | |
|
||||
* | RS512 | RSASSA-PKCS1-v1_5 using | Optional |
|
||||
* | | SHA-512 | |
|
||||
* | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
|
||||
* | ES384 | ECDSA using P-384 and SHA-384 | Optional |
|
||||
* | ES512 | ECDSA using P-521 and SHA-512 | Optional |
|
||||
*
|
||||
* Boulder (FOSS ACME provider) supports RS256, ES256, ES384 and ES512
|
||||
* currently. The "Recommended+" just means it is recommended but will likely
|
||||
* be "very recommended" soon.
|
||||
*
|
||||
* We support HS256/384/512 for symmetric crypto, but the choice for the
|
||||
* asymmetric crypto isn't as easy to make.
|
||||
*
|
||||
* Normally you'd choose the EC option but these are defined to use the
|
||||
* "NIST curves" (RFC7518 3.4) which are believed to be insecure.
|
||||
*
|
||||
* https://safecurves.cr.yp.to/
|
||||
*
|
||||
* For that reason we implement RS256/384/512 for asymmetric.
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_SELFTESTS)
|
||||
static const char
|
||||
*test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
|
||||
*test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
|
||||
*test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
|
||||
" \"http://example.com/is_root\":true}",
|
||||
*test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
|
||||
"ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
|
||||
*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 =
|
||||
"{\"kty\":\"RSA\","
|
||||
" \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
|
||||
"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
|
||||
"D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
|
||||
"SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
|
||||
"MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
|
||||
"NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
|
||||
"jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
|
||||
"BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
|
||||
"439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
|
||||
"CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
|
||||
"BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
|
||||
"\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
|
||||
"YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
|
||||
"BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
|
||||
"\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
|
||||
"ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
|
||||
"-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
|
||||
"\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
|
||||
"CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
|
||||
"34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
|
||||
"\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
|
||||
"7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
|
||||
"NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
|
||||
"\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
|
||||
"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";
|
||||
#endif
|
||||
|
||||
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)
|
||||
return n; /* too large for output buffer */
|
||||
|
||||
/* trim the terminal = */
|
||||
while (n && out[n - 1] == '=')
|
||||
n--;
|
||||
|
||||
out[n] = '\0';
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
|
||||
char *end)
|
||||
{
|
||||
int n, len = (end - *p) - 1;
|
||||
char *p_entry = *p;
|
||||
|
||||
if (len < 3)
|
||||
return -1;
|
||||
|
||||
if (!first)
|
||||
*(*p)++ = '.';
|
||||
|
||||
n = lws_jws_base64_enc(in, in_len, *p, len - 1);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
*p += n;
|
||||
|
||||
return (*p) - p_entry;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_jws_find_sig(const char *in, size_t len)
|
||||
{
|
||||
const char *p = in + len - 1;
|
||||
|
||||
while (len--)
|
||||
if (*p == '.')
|
||||
return (p + 1) - in;
|
||||
else
|
||||
p--;
|
||||
|
||||
lwsl_notice("%s failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static const char * const jhdr_tok[] = {
|
||||
"typ",
|
||||
"alg",
|
||||
};
|
||||
enum enum_jhdr_tok {
|
||||
JHP_TYP,
|
||||
JHP_ALG
|
||||
};
|
||||
struct cb_hdr_s {
|
||||
enum lws_genhash_types hash_type;
|
||||
enum lws_genhmac_types hmac_type;
|
||||
char alg[10];
|
||||
int is_rsa:1;
|
||||
};
|
||||
|
||||
static signed char
|
||||
cb_hdr(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct cb_hdr_s *s = (struct cb_hdr_s *)ctx->user;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
case JHP_TYP: /* it is optional */
|
||||
if (strcmp(ctx->buf, "JWT"))
|
||||
return -1;
|
||||
break;
|
||||
case JHP_ALG:
|
||||
lws_strncpy(s->alg, ctx->buf, sizeof(s->alg));
|
||||
if (!strcmp(ctx->buf, "HS256")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA256;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "HS384")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA384;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "HS512")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA512;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS256")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA256;
|
||||
s->is_rsa = 1;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS384")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA384;
|
||||
s->is_rsa = 1;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS512")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA512;
|
||||
s->is_rsa = 1;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk)
|
||||
{
|
||||
int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
struct lws_genrsa_ctx rsactx;
|
||||
struct lws_genhmac_ctx ctx;
|
||||
struct cb_hdr_s args;
|
||||
struct lejp_ctx jctx;
|
||||
char buf[2048];
|
||||
|
||||
/* 1) there has to be a signature */
|
||||
|
||||
if (sig_pos < 0)
|
||||
return -1;
|
||||
|
||||
/* 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
|
||||
*/
|
||||
|
||||
args.alg[0] = '\0';
|
||||
args.is_rsa = 0;
|
||||
lejp_construct(&jctx, cb_hdr, &args, jhdr_tok, LWS_ARRAY_SIZE(jhdr_tok));
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
|
||||
lejp_destruct(&jctx);
|
||||
if (m < 0) {
|
||||
lwsl_notice("parse got %d: alg %s\n", m, args.alg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 5) decode the B64URL signature part into buf / m */
|
||||
|
||||
m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
|
||||
buf, sizeof(buf) - 1);
|
||||
|
||||
if (args.is_rsa) {
|
||||
|
||||
/* RSASSA-PKCS1-v1_5 using SHA-256/384/512 */
|
||||
|
||||
/* 6(RSA): compute the hash of the payload into "digest" */
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, args.hash_type))
|
||||
return -1;
|
||||
|
||||
if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
|
||||
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);
|
||||
|
||||
if (lws_genrsa_create(&rsactx, &jwk->el)) {
|
||||
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = lws_genrsa_public_verify(&rsactx, digest, args.hash_type,
|
||||
(uint8_t *)buf, m);
|
||||
|
||||
lws_genrsa_destroy(&rsactx);
|
||||
if (n < 0) {
|
||||
lwsl_notice("decrypt fail\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SHA256/384/512 HMAC */
|
||||
|
||||
h_len = lws_genhmac_size(args.hmac_type);
|
||||
if (m < 0 || m != h_len)
|
||||
return -1;
|
||||
|
||||
/* 6) compute HMAC over payload */
|
||||
|
||||
if (lws_genhmac_init(&ctx, args.hmac_type, jwk->el.e[JWK_KEY_E].buf,
|
||||
jwk->el.e[JWK_KEY_E].len))
|
||||
return -1;
|
||||
|
||||
if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
|
||||
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)) {
|
||||
lwsl_notice("digest mismatch\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
enum lws_genhash_types hash_type, struct lws_jwk *jwk)
|
||||
{
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
struct lws_genrsa_ctx rsactx;
|
||||
uint8_t *buf;
|
||||
int n;
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, 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;
|
||||
|
||||
if (!strcmp(jwk->keytype, "RSA")) {
|
||||
if (lws_genrsa_create(&rsactx, &jwk->el)) {
|
||||
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = jwk->el.e[JWK_KEY_N].len;
|
||||
buf = lws_malloc(n, "jws sign");
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_genrsa_public_sign(&rsactx, digest, hash_type, buf, n);
|
||||
lws_genrsa_destroy(&rsactx);
|
||||
if (n < 0) {
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
|
||||
lws_free(buf);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
if (!strcmp(jwk->keytype, "oct"))
|
||||
return lws_jws_base64_enc((char *)digest,
|
||||
lws_genhash_size(hash_type),
|
||||
b64_sig, sig_len);
|
||||
|
||||
/* unknown key type */
|
||||
|
||||
return -1;
|
||||
|
||||
hash_fail:
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
|
||||
const char *nonce, char *out, size_t out_len)
|
||||
{
|
||||
char *buf, *start, *p, *end, *p1, *end1, *b64_hdr, *b64_pay;
|
||||
int n, b64_hdr_len, b64_pay_len;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
n = LWS_PRE + 2048;
|
||||
buf = malloc(n);
|
||||
if (!buf) {
|
||||
lwsl_notice("%s: malloc %d failed\n", __func__, n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = start = buf + LWS_PRE;
|
||||
end = buf + n - LWS_PRE - 1;
|
||||
|
||||
/*
|
||||
* temporary JWS protected header plaintext
|
||||
*/
|
||||
|
||||
p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
|
||||
n = lws_jwk_export(jwk, 0, p, end - p);
|
||||
if (n < 0) {
|
||||
lwsl_notice("failed to export jwk\n");
|
||||
|
||||
goto bail;
|
||||
}
|
||||
p += n;
|
||||
p += lws_snprintf(p, end - p, ",\"nonce\":\"%s\"}", nonce);
|
||||
|
||||
/*
|
||||
* prepare the signed outer JSON with all the parts in
|
||||
*/
|
||||
|
||||
p1 = out;
|
||||
end1 = out + out_len - 1;
|
||||
|
||||
p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\"");
|
||||
b64_hdr = 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;
|
||||
p1 += n;
|
||||
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
|
||||
b64_pay = 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;
|
||||
|
||||
p1 += n;
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\"");
|
||||
|
||||
/*
|
||||
* 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, LWS_GENHASH_TYPE_SHA256, jwk);
|
||||
if (n < 0) {
|
||||
lwsl_notice("sig gen failed\n");
|
||||
|
||||
goto bail;
|
||||
}
|
||||
p1 += n;
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\"}");
|
||||
|
||||
free(buf);
|
||||
|
||||
return p1 - out;
|
||||
|
||||
bail:
|
||||
free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#if defined(LWS_WITH_SELFTESTS)
|
||||
/*
|
||||
* 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
|
||||
lws_jws_selftest(void)
|
||||
{
|
||||
struct lws_genhmac_ctx ctx;
|
||||
struct lws_jwk jwk;
|
||||
char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
int n;
|
||||
|
||||
/* Test 1: SHA256 on RFC7515 worked example */
|
||||
|
||||
/* 1.1: decode the JWK oct key */
|
||||
|
||||
if (lws_jwk_import(&jwk, key_jwk, strlen(key_jwk)) < 0) {
|
||||
lwsl_notice("Failed to decode JWK test key\n");
|
||||
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.el.e[JWK_KEY_E].buf, jwk.el.e[JWK_KEY_E].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 */
|
||||
goto bail;
|
||||
|
||||
/* 1.5: Check we can agree the signature matches the payload */
|
||||
|
||||
if (lws_jws_confirm_sig(buf, p - buf, &jwk) < 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, rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
|
||||
lwsl_notice("Failed to read JWK key\n");
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* 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) < 0) {
|
||||
lwsl_notice("confirm rsa sig failed\n");
|
||||
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, '.');
|
||||
|
||||
n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
|
||||
p1 + 1, sizeof(buf) - (p1 - buf) - 1,
|
||||
LWS_GENHASH_TYPE_SHA256, &jwk);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
puts(buf);
|
||||
|
||||
/* 2.4: confirm our signature can be verified */
|
||||
|
||||
if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk) < 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;
|
||||
}
|
||||
#endif
|
0
lib/tls/mbedtls/lws-genec.c
Normal file
0
lib/tls/mbedtls/lws-genec.c
Normal file
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - generic RSA api hiding the backend
|
||||
*
|
||||
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2017 - 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
|
||||
|
@ -22,40 +22,39 @@
|
|||
* same whether you are using openssl or mbedtls hash functions underneath.
|
||||
*/
|
||||
#include "core/private.h"
|
||||
#include "../../jose/private.h"
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el)
|
||||
lws_jwk_destroy_genrsa_elements(struct lws_jwk_elements *el)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
|
||||
if (el->e[n].buf)
|
||||
lws_free_set_NULL(el->e[n].buf);
|
||||
lws_jwk_destroy_elements(el, LWS_COUNT_RSA_KEY_ELEMENTS);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
|
||||
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_jwk_elements *el,
|
||||
struct lws_context *context)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->ctx = lws_zalloc(sizeof(*ctx->ctx), "genrsa");
|
||||
if (!ctx->ctx)
|
||||
return 1;
|
||||
|
||||
ctx->context = context;
|
||||
mbedtls_rsa_init(ctx->ctx, MBEDTLS_RSA_PKCS_V15, 0);
|
||||
|
||||
{
|
||||
int n;
|
||||
|
||||
mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
|
||||
mbedtls_mpi *mpi[LWS_COUNT_RSA_KEY_ELEMENTS] = {
|
||||
&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
|
||||
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
|
||||
&ctx->ctx->QP,
|
||||
};
|
||||
|
||||
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
|
||||
if (el->e[n].buf &&
|
||||
mbedtls_mpi_read_binary(mpi[n], el->e[n].buf,
|
||||
el->e[n].len)) {
|
||||
for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++)
|
||||
if (el[n].buf &&
|
||||
mbedtls_mpi_read_binary(mpi[n], el[n].buf,
|
||||
el[n].len)) {
|
||||
lwsl_notice("mpi load failed\n");
|
||||
lws_free_set_NULL(ctx->ctx);
|
||||
|
||||
|
@ -63,7 +62,7 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
|
|||
}
|
||||
}
|
||||
|
||||
ctx->ctx->len = el->e[JWK_KEY_N].len;
|
||||
ctx->ctx->len = el[JWK_RSA_KEYEL_N].len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -79,7 +78,7 @@ _rngf(void *context, unsigned char *buf, size_t len)
|
|||
|
||||
LWS_VISIBLE int
|
||||
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
||||
struct lws_genrsa_elements *el, int bits)
|
||||
struct lws_jwk_elements *el, int bits)
|
||||
{
|
||||
int n;
|
||||
|
||||
|
@ -97,30 +96,30 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
|||
}
|
||||
|
||||
{
|
||||
mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
|
||||
mbedtls_mpi *mpi[LWS_COUNT_RSA_KEY_ELEMENTS] = {
|
||||
&ctx->ctx->E, &ctx->ctx->N, &ctx->ctx->D, &ctx->ctx->P,
|
||||
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
|
||||
&ctx->ctx->QP,
|
||||
};
|
||||
|
||||
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
|
||||
for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++)
|
||||
if (mbedtls_mpi_size(mpi[n])) {
|
||||
el->e[n].buf = lws_malloc(
|
||||
el[n].buf = lws_malloc(
|
||||
mbedtls_mpi_size(mpi[n]), "genrsakey");
|
||||
if (!el->e[n].buf)
|
||||
if (!el[n].buf)
|
||||
goto cleanup;
|
||||
el->e[n].len = mbedtls_mpi_size(mpi[n]);
|
||||
mbedtls_mpi_write_binary(mpi[n], el->e[n].buf,
|
||||
el->e[n].len);
|
||||
el[n].len = mbedtls_mpi_size(mpi[n]);
|
||||
mbedtls_mpi_write_binary(mpi[n], el[n].buf,
|
||||
el[n].len);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
|
||||
if (el->e[n].buf)
|
||||
lws_free_set_NULL(el->e[n].buf);
|
||||
for (n = 0; n < LWS_COUNT_JWK_ELEMENTS; n++)
|
||||
if (el[n].buf)
|
||||
lws_free_set_NULL(el[n].buf);
|
||||
cleanup_1:
|
||||
lws_free(ctx->ctx);
|
||||
|
||||
|
@ -153,12 +152,14 @@ lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
|||
{
|
||||
int n;
|
||||
|
||||
ctx->ctx->len = in_len;
|
||||
n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, NULL, NULL,
|
||||
//ctx->ctx->len = in_len; // ???
|
||||
ctx->ctx->padding = MBEDTLS_RSA_PKCS_V15;
|
||||
n = mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx->ctx, _rngf, ctx->context,
|
||||
MBEDTLS_RSA_PRIVATE,
|
||||
in_len, in, out);
|
||||
if (n) {
|
||||
lwsl_notice("%s: -0x%x\n", __func__, -n);
|
||||
lwsl_notice("%s: -0x%x: in_len: %d\n", __func__, -n,
|
||||
(int)in_len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -245,7 +246,7 @@ lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
|
|||
uint8_t *pkey_asn1, size_t pkey_asn1_len)
|
||||
{
|
||||
uint8_t *p = pkey_asn1, *totlen, *end = pkey_asn1 + pkey_asn1_len - 1;
|
||||
mbedtls_mpi *mpi[LWS_COUNT_RSA_ELEMENTS] = {
|
||||
mbedtls_mpi *mpi[LWS_COUNT_RSA_KEY_ELEMENTS] = {
|
||||
&ctx->ctx->N, &ctx->ctx->E, &ctx->ctx->D, &ctx->ctx->P,
|
||||
&ctx->ctx->Q, &ctx->ctx->DP, &ctx->ctx->DQ,
|
||||
&ctx->ctx->QP,
|
||||
|
@ -277,7 +278,7 @@ lws_genrsa_render_pkey_asn1(struct lws_genrsa_ctx *ctx, int _private,
|
|||
*p++ = 0x01;
|
||||
*p++ = 0x00;
|
||||
|
||||
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++) {
|
||||
for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++) {
|
||||
int m = mbedtls_mpi_size(mpi[n]);
|
||||
uint8_t *elen;
|
||||
|
||||
|
|
|
@ -453,7 +453,7 @@ lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
|
|||
int buflen = 0x560;
|
||||
uint8_t *buf = lws_malloc(buflen, "tmp cert buf"), *p = buf, *pkey_asn1;
|
||||
struct lws_genrsa_ctx ctx;
|
||||
struct lws_genrsa_elements el;
|
||||
struct lws_jwk_elements el;
|
||||
uint8_t digest[32];
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
int pkey_asn1_len = 3 * 1024;
|
||||
|
@ -498,8 +498,8 @@ lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a,
|
|||
/* we need to drop 1 + (keybits / 8) bytes of n in here, 00 + key */
|
||||
|
||||
*p++ = 0x00;
|
||||
memcpy(p, el.e[JWK_KEY_N].buf, el.e[JWK_KEY_N].len);
|
||||
p += el.e[JWK_KEY_N].len;
|
||||
memcpy(p, el.e[JWK_RSA_KEYEL_N].buf, el.e[JWK_RSA_KEYEL_N].len);
|
||||
p += el.e[JWK_RSA_KEYEL_N].len;
|
||||
|
||||
memcpy(p, ss_cert_san_leadin, sizeof(ss_cert_san_leadin));
|
||||
p += sizeof(ss_cert_san_leadin);
|
||||
|
|
0
lib/tls/openssl/lws-genec.c
Normal file
0
lib/tls/openssl/lws-genec.c
Normal file
|
@ -22,31 +22,31 @@
|
|||
* same whether you are using openssl or mbedtls hash functions underneath.
|
||||
*/
|
||||
#include "core/private.h"
|
||||
#include "../../jose/private.h"
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el)
|
||||
lws_jwk_destroy_genrsa_elements(struct lws_jwk_elements *el)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
|
||||
if (el->e[n].buf)
|
||||
lws_free_set_NULL(el->e[n].buf);
|
||||
lws_jwk_destroy_elements(el, LWS_COUNT_RSA_KEY_ELEMENTS);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
|
||||
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_jwk_elements *el,
|
||||
struct lws_context *context)
|
||||
{
|
||||
int n;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
ctx->context = context;
|
||||
|
||||
/* Step 1:
|
||||
*
|
||||
* convert the MPI for e and n to OpenSSL BIGNUMs
|
||||
*/
|
||||
|
||||
for (n = 0; n < 5; n++) {
|
||||
ctx->bn[n] = BN_bin2bn(el->e[n].buf, el->e[n].len, NULL);
|
||||
ctx->bn[n] = BN_bin2bn(el[n].buf, el[n].len, NULL);
|
||||
if (!ctx->bn[n]) {
|
||||
lwsl_notice("mpi load failed\n");
|
||||
goto bail;
|
||||
|
@ -65,18 +65,20 @@ lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el)
|
|||
}
|
||||
|
||||
#if defined(LWS_HAVE_RSA_SET0_KEY)
|
||||
if (RSA_set0_key(ctx->rsa, ctx->bn[JWK_KEY_N], ctx->bn[JWK_KEY_E],
|
||||
ctx->bn[JWK_KEY_D]) != 1) {
|
||||
if (RSA_set0_key(ctx->rsa, ctx->bn[JWK_RSA_KEYEL_N],
|
||||
ctx->bn[JWK_RSA_KEYEL_E],
|
||||
ctx->bn[JWK_RSA_KEYEL_D]) != 1) {
|
||||
lwsl_notice("RSA_set0_key failed\n");
|
||||
goto bail;
|
||||
}
|
||||
RSA_set0_factors(ctx->rsa, ctx->bn[JWK_KEY_P], ctx->bn[JWK_KEY_Q]);
|
||||
RSA_set0_factors(ctx->rsa, ctx->bn[JWK_RSA_KEYEL_P],
|
||||
ctx->bn[JWK_RSA_KEYEL_Q]);
|
||||
#else
|
||||
ctx->rsa->e = ctx->bn[JWK_KEY_E];
|
||||
ctx->rsa->n = ctx->bn[JWK_KEY_N];
|
||||
ctx->rsa->d = ctx->bn[JWK_KEY_D];
|
||||
ctx->rsa->p = ctx->bn[JWK_KEY_P];
|
||||
ctx->rsa->q = ctx->bn[JWK_KEY_Q];
|
||||
ctx->rsa->e = ctx->bn[JWK_RSA_KEYEL_E];
|
||||
ctx->rsa->n = ctx->bn[JWK_RSA_KEYEL_N];
|
||||
ctx->rsa->d = ctx->bn[JWK_RSA_KEYEL_D];
|
||||
ctx->rsa->p = ctx->bn[JWK_RSA_KEYEL_P];
|
||||
ctx->rsa->q = ctx->bn[JWK_RSA_KEYEL_Q];
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
@ -98,7 +100,7 @@ bail:
|
|||
|
||||
LWS_VISIBLE int
|
||||
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
||||
struct lws_genrsa_elements *el, int bits)
|
||||
struct lws_jwk_elements *el, int bits)
|
||||
{
|
||||
BIGNUM *bn;
|
||||
int n;
|
||||
|
@ -128,9 +130,10 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
|||
{
|
||||
const BIGNUM *mpi[5];
|
||||
|
||||
RSA_get0_key(ctx->rsa, &mpi[JWK_KEY_N], &mpi[JWK_KEY_E],
|
||||
&mpi[JWK_KEY_D]);
|
||||
RSA_get0_factors(ctx->rsa, &mpi[JWK_KEY_P], &mpi[JWK_KEY_Q]);
|
||||
RSA_get0_key(ctx->rsa, &mpi[JWK_RSA_KEYEL_N],
|
||||
&mpi[JWK_RSA_KEYEL_E], &mpi[JWK_RSA_KEYEL_D]);
|
||||
RSA_get0_factors(ctx->rsa, &mpi[JWK_RSA_KEYEL_P],
|
||||
&mpi[JWK_RSA_KEYEL_Q]);
|
||||
#else
|
||||
{
|
||||
BIGNUM *mpi[5] = {
|
||||
|
@ -140,41 +143,66 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
|||
#endif
|
||||
for (n = 0; n < 5; n++)
|
||||
if (BN_num_bytes(mpi[n])) {
|
||||
el->e[n].buf = lws_malloc(
|
||||
el[n].buf = lws_malloc(
|
||||
BN_num_bytes(mpi[n]), "genrsakey");
|
||||
if (!el->e[n].buf)
|
||||
if (!el[n].buf)
|
||||
goto cleanup;
|
||||
el->e[n].len = BN_num_bytes(mpi[n]);
|
||||
BN_bn2bin(mpi[n], el->e[n].buf);
|
||||
el[n].len = BN_num_bytes(mpi[n]);
|
||||
BN_bn2bin(mpi[n], el[n].buf);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
for (n = 0; n < LWS_COUNT_RSA_ELEMENTS; n++)
|
||||
if (el->e[n].buf)
|
||||
lws_free_set_NULL(el->e[n].buf);
|
||||
for (n = 0; n < LWS_COUNT_RSA_KEY_ELEMENTS; n++)
|
||||
if (el[n].buf)
|
||||
lws_free_set_NULL(el[n].buf);
|
||||
cleanup_1:
|
||||
RSA_free(ctx->rsa);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* flen must be less than RSA_size(rsa) - 11 for the PKCS #1 v1.5
|
||||
* based padding modes
|
||||
*/
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
||||
size_t in_len, uint8_t *out)
|
||||
{
|
||||
int m;
|
||||
|
||||
m = RSA_public_encrypt((int)in_len, in, out, ctx->rsa,
|
||||
RSA_PKCS1_PADDING);
|
||||
|
||||
/* the bignums are also freed by freeing the RSA */
|
||||
RSA_free(ctx->rsa);
|
||||
ctx->rsa = NULL;
|
||||
|
||||
if (m != -1)
|
||||
return m;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint32_t m;
|
||||
int m;
|
||||
|
||||
m = RSA_public_decrypt((int)in_len, in, out, ctx->rsa, RSA_PKCS1_PADDING);
|
||||
m = RSA_public_decrypt((int)in_len, in, out, ctx->rsa,
|
||||
RSA_PKCS1_PADDING);
|
||||
|
||||
/* the bignums are also freed by freeing the RSA */
|
||||
RSA_free(ctx->rsa);
|
||||
ctx->rsa = NULL;
|
||||
|
||||
if (m != (uint32_t)-1)
|
||||
return (int)m;
|
||||
if (m != -1)
|
||||
return m;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
|
73
minimal-examples/api-tests/api-test-jose/CMakeLists.txt
Normal file
73
minimal-examples/api-tests/api-test-jose/CMakeLists.txt
Normal file
|
@ -0,0 +1,73 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-api-test-jose)
|
||||
set(SRCS main.c jwk.c jws.c jwe.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()
|
||||
|
||||
|
||||
|
||||
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()
|
22
minimal-examples/api-tests/api-test-jose/README.md
Normal file
22
minimal-examples/api-tests/api-test-jose/README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# lws api test lwsac
|
||||
|
||||
Demonstrates how to use and performs selftests for lwsac
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
|
||||
```
|
||||
$ ./lws-api-test-lwsac
|
||||
[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac
|
||||
[2018/10/09 09:14:17:4835] USER: Completed: PASS
|
||||
```
|
||||
|
332
minimal-examples/api-tests/api-test-jose/jwe.c
Normal file
332
minimal-examples/api-tests/api-test-jose/jwe.c
Normal file
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* lws-api-test-jose - RFC7516 jwe tests
|
||||
*
|
||||
* 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>
|
||||
|
||||
|
||||
/* A.2. Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256 */
|
||||
|
||||
/* "Live long and prosper." */
|
||||
static
|
||||
uint8_t
|
||||
|
||||
#if 0
|
||||
lws_jwe_ex_a2_plaintext[] = {
|
||||
76, 105, 118, 101, 32, 108, 111, 110,
|
||||
103, 32, 97, 110, 100, 32, 112, 114,
|
||||
111, 115, 112, 101, 114, 46
|
||||
},
|
||||
#endif
|
||||
*lws_jwe_ex_a2_jose_hdr = (uint8_t *)
|
||||
"{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}",
|
||||
|
||||
*lws_jwe_ex_a2_jose_hdr_b64utf8 = (unsigned char *)
|
||||
"eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
|
||||
|
||||
lws_jwe_ex_a2_cek[] = {
|
||||
4, 211, 31, 197, 84, 157, 252, 254,
|
||||
11, 100, 157, 250, 63, 170, 106, 206,
|
||||
107, 124, 212, 45, 111, 107, 9, 219,
|
||||
200, 177, 0, 240, 143, 156, 44, 207
|
||||
},
|
||||
|
||||
*lws_jwe_ex_a2_jwk_json = (uint8_t *)
|
||||
"{"
|
||||
"\"kty\":\"RSA\","
|
||||
"\"n\":\"sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1Wl"
|
||||
"UzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDpre"
|
||||
"cbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_"
|
||||
"7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBI"
|
||||
"Y2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU"
|
||||
"7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"d\":\"VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq"
|
||||
"1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-ry"
|
||||
"nq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_"
|
||||
"0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj"
|
||||
"-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-Kyvj"
|
||||
"T1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ\","
|
||||
"\"p\":\"9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68"
|
||||
"ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEP"
|
||||
"krdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM\","
|
||||
"\"q\":\"uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-y"
|
||||
"BhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN"
|
||||
"-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0\","
|
||||
"\"dp\":\"w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuv"
|
||||
"ngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcra"
|
||||
"Hawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs\","
|
||||
"\"dq\":\"o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff"
|
||||
"7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_"
|
||||
"odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU\","
|
||||
"\"qi\":\"eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlC"
|
||||
"tUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZ"
|
||||
"B9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo\""
|
||||
"}",
|
||||
|
||||
lws_jwe_ex_a2_jwk_enc_key[] = {
|
||||
80, 104, 72, 58, 11, 130, 236, 139,
|
||||
132, 189, 255, 205, 61, 86, 151, 176,
|
||||
99, 40, 44, 233, 176, 189, 205, 70,
|
||||
202, 169, 72, 40, 226, 181, 156, 223,
|
||||
120, 156, 115, 232, 150, 209, 145, 133,
|
||||
104, 112, 237, 156, 116, 250, 65, 102,
|
||||
212, 210, 103, 240, 177, 61, 93, 40,
|
||||
71, 231, 223, 226, 240, 157, 15, 31,
|
||||
150, 89, 200, 215, 198, 203, 108, 70,
|
||||
117, 66, 212, 238, 193, 205, 23, 161,
|
||||
169, 218, 243, 203, 128, 214, 127, 253,
|
||||
215, 139, 43, 17, 135, 103, 179, 220,
|
||||
28, 2, 212, 206, 131, 158, 128, 66,
|
||||
62, 240, 78, 186, 141, 125, 132, 227,
|
||||
60, 137, 43, 31, 152, 199, 54, 72,
|
||||
34, 212, 115, 11, 152, 101, 70, 42,
|
||||
219, 233, 142, 66, 151, 250, 126, 146,
|
||||
141, 216, 190, 73, 50, 177, 146, 5,
|
||||
52, 247, 28, 197, 21, 59, 170, 247,
|
||||
181, 89, 131, 241, 169, 182, 246, 99,
|
||||
15, 36, 102, 166, 182, 172, 197, 136,
|
||||
230, 120, 60, 58, 219, 243, 149, 94,
|
||||
222, 150, 154, 194, 110, 227, 225, 112,
|
||||
39, 89, 233, 112, 207, 211, 241, 124,
|
||||
174, 69, 221, 179, 107, 196, 225, 127,
|
||||
167, 112, 226, 12, 242, 16, 24, 28,
|
||||
120, 182, 244, 213, 244, 153, 194, 162,
|
||||
69, 160, 244, 248, 63, 165, 141, 4,
|
||||
207, 249, 193, 79, 131, 0, 169, 233,
|
||||
127, 167, 101, 151, 125, 56, 112, 111,
|
||||
248, 29, 232, 90, 29, 147, 110, 169,
|
||||
146, 114, 165, 204, 71, 136, 41, 252
|
||||
}
|
||||
#if 0
|
||||
,
|
||||
*lws_jwe_ex_a2_jwk_enc_key_b64 = (uint8_t *)
|
||||
"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm"
|
||||
"1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc"
|
||||
"HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF"
|
||||
"NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8"
|
||||
"rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv"
|
||||
"-B3oWh2TbqmScqXMR4gp_A",
|
||||
|
||||
lws_jwe_ex_a2_iv[] = {
|
||||
3, 22, 60, 12, 43, 67, 104, 105,
|
||||
108, 108, 105, 99, 111, 116, 104, 101
|
||||
},
|
||||
|
||||
*lws_jwe_ex_a2_iv_b64 = (uint8_t *)
|
||||
"AxY8DCtDaGlsbGljb3RoZQ",
|
||||
|
||||
lws_jwe_ex_a2_aad[] = {
|
||||
101, 121, 74, 104, 98, 71, 99, 105,
|
||||
79, 105, 74, 83, 85, 48, 69, 120,
|
||||
88, 122, 85, 105, 76, 67, 74, 108,
|
||||
98, 109, 77, 105, 79, 105, 74, 66,
|
||||
77, 84, 73, 52, 81, 48, 74, 68,
|
||||
76, 85, 104, 84, 77, 106, 85, 50,
|
||||
73, 110, 48
|
||||
},
|
||||
|
||||
lws_jwe_ex_a2_ciphertext[] = {
|
||||
40, 57, 83, 181, 119, 33, 133, 148,
|
||||
198, 185, 243, 24, 152, 230, 6, 75,
|
||||
129, 223, 127, 19, 210, 82, 183, 230,
|
||||
168, 33, 215, 104, 143, 112, 56, 102
|
||||
},
|
||||
|
||||
*lws_jwe_ex_a2_ciphertext_b64 = (uint8_t *)
|
||||
"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",
|
||||
|
||||
lws_jwe_ex_a2_authtag[] = {
|
||||
246, 17, 244, 190, 4, 95, 98, 3,
|
||||
231, 0, 115, 157, 242, 203, 100, 191
|
||||
},
|
||||
|
||||
*lws_jwe_ex_a2_authtag_b64 = (uint8_t *)
|
||||
"9hH0vgRfYgPnAHOd8stkvw",
|
||||
|
||||
*lws_jwe_ex_a2_aggregated = (uint8_t *)
|
||||
"eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0."
|
||||
"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm"
|
||||
"1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc"
|
||||
"HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF"
|
||||
"NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8"
|
||||
"rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv"
|
||||
"-B3oWh2TbqmScqXMR4gp_A."
|
||||
"AxY8DCtDaGlsbGljb3RoZQ."
|
||||
"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY."
|
||||
"9hH0vgRfYgPnAHOd8stkvw"
|
||||
#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_jwe(struct lws_context *context)
|
||||
{
|
||||
struct lws_genrsa_ctx rsactx;
|
||||
struct lws_jwk jwk;
|
||||
uint8_t enc_cek[sizeof(lws_jwe_ex_a2_jwk_enc_key) + 2048];
|
||||
char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1;
|
||||
int n;
|
||||
|
||||
/* Test 1: A.2 */
|
||||
|
||||
/* Decode the JWK JSON key */
|
||||
|
||||
if (lws_jwk_import(&jwk, NULL, NULL, (char *)lws_jwe_ex_a2_jwk_json,
|
||||
strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) {
|
||||
lwsl_notice("Failed to decode JWK test key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (jwk.kty != LWS_JWK_KYT_RSA) {
|
||||
lwsl_err("%s: unexpected kty %d\n", __func__, jwk.kty);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* A.2.1: encode JOSE header and confirm matches official string */
|
||||
|
||||
n = lws_jws_encode_section((char *)lws_jwe_ex_a2_jose_hdr,
|
||||
strlen((char *)lws_jwe_ex_a2_jose_hdr), 1,
|
||||
&p, end);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
if (strcmp(buf, (char *)lws_jwe_ex_a2_jose_hdr_b64utf8))
|
||||
goto bail;
|
||||
|
||||
/* A.2.3: Encrypt the CEK with the recipient's public key using the
|
||||
* RSAES-PKCS1-v1_5 algorithm to produce the JWE Encrypted Key.
|
||||
*/
|
||||
|
||||
if (lws_genrsa_create(&rsactx, jwk.e, context)) {
|
||||
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
|
||||
__func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
memset(enc_cek, 0, sizeof(enc_cek));
|
||||
|
||||
n = lws_genrsa_public_encrypt(&rsactx, lws_jwe_ex_a2_cek,
|
||||
sizeof(lws_jwe_ex_a2_cek), enc_cek);
|
||||
lws_genrsa_destroy(&rsactx);
|
||||
if (n < 0) {
|
||||
lwsl_err("%s: encrypt cek fail\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
#if 0
|
||||
if (memcmp(enc_cek, lws_jwe_ex_a2_jwk_enc_key, sizeof(enc_cek))) {
|
||||
lwsl_err("%s: encrypt cek wrong output\n", __func__);
|
||||
lwsl_hexdump_notice(enc_cek, sizeof(enc_cek));
|
||||
lwsl_hexdump_notice(lws_jwe_ex_a2_jwk_enc_key,
|
||||
sizeof(lws_jwe_ex_a2_jwk_enc_key));
|
||||
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.el.e[JWK_RSA_KEYEL_E].buf,
|
||||
jwk.el.e[JWK_RSA_KEYEL_E].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 */
|
||||
goto bail;
|
||||
|
||||
/* 1.5: Check we can agree the signature matches the payload */
|
||||
|
||||
if (lws_jws_confirm_sig(buf, p - buf, &jwk) < 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, rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
|
||||
lwsl_notice("Failed to read JWK key\n");
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* 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) < 0) {
|
||||
lwsl_notice("confirm rsa sig failed\n");
|
||||
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, '.');
|
||||
|
||||
n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
|
||||
p1 + 1, sizeof(buf) - (p1 - buf) - 1,
|
||||
LWS_GENHASH_TYPE_SHA256, &jwk);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
puts(buf);
|
||||
|
||||
/* 2.4: confirm our signature can be verified */
|
||||
|
||||
if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk) < 0) {
|
||||
lwsl_notice("confirm rsa sig 2 failed\n");
|
||||
goto bail;
|
||||
}
|
||||
#endif
|
||||
lws_jwk_destroy(&jwk);
|
||||
|
||||
/* end */
|
||||
|
||||
lwsl_notice("%s: selftest OK\n", __func__);
|
||||
|
||||
return 0;
|
||||
#if 0
|
||||
bail_destroy_hmac:
|
||||
lws_genhmac_destroy(&ctx, NULL);
|
||||
#endif
|
||||
bail:
|
||||
lws_jwk_destroy(&jwk);
|
||||
//bail2:
|
||||
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
350
minimal-examples/api-tests/api-test-jose/jwk.c
Normal file
350
minimal-examples/api-tests/api-test-jose/jwk.c
Normal file
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* lws-api-test-jose - RFC7517 jwk tests
|
||||
*
|
||||
* 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>
|
||||
|
||||
static
|
||||
uint8_t *lws_jwe_ex_a1_jwk_json = (uint8_t *) /* EC + RSA public keys */
|
||||
"{\"keys\":"
|
||||
"["
|
||||
"{\"kty\":\"EC\","
|
||||
"\"crv\":\"P-256\","
|
||||
"\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","
|
||||
"\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\","
|
||||
"\"use\":\"enc\","
|
||||
"\"kid\":\"1\"},"
|
||||
|
||||
"{\"kty\":\"RSA\","
|
||||
"\"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx"
|
||||
"4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs"
|
||||
"tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2"
|
||||
"QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI"
|
||||
"SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb"
|
||||
"w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"alg\":\"RS256\","
|
||||
"\"kid\":\"2011-04-29\"}"
|
||||
"]"
|
||||
"}",
|
||||
|
||||
*lws_jwe_ex_a2_jwk_json = (uint8_t *) /* EC + RSA private keys */
|
||||
"{\"keys\":"
|
||||
"["
|
||||
"{\"kty\":\"EC\","
|
||||
"\"crv\":\"P-256\","
|
||||
"\"x\":\"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\","
|
||||
"\"y\":\"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\","
|
||||
"\"d\":\"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE\","
|
||||
"\"use\":\"enc\","
|
||||
"\"kid\":\"1\"},"
|
||||
|
||||
"{\"kty\":\"RSA\","
|
||||
"\"n\":\"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4"
|
||||
"cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst"
|
||||
"n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2Q"
|
||||
"vzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbIS"
|
||||
"D08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw"
|
||||
"0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"d\":\"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9"
|
||||
"M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqij"
|
||||
"wp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d"
|
||||
"_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBz"
|
||||
"nbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFz"
|
||||
"me1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q\","
|
||||
"\"p\":\"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPV"
|
||||
"nwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqV"
|
||||
"WlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs\","
|
||||
"\"q\":\"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyum"
|
||||
"qjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgx"
|
||||
"kIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk\","
|
||||
"\"dp\":\"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oim"
|
||||
"YwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_Nmtu"
|
||||
"YZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0\","
|
||||
"\"dq\":\"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUU"
|
||||
"vMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9"
|
||||
"GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk\","
|
||||
"\"qi\":\"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzg"
|
||||
"UIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rx"
|
||||
"yR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU\","
|
||||
"\"alg\":\"RS256\","
|
||||
"\"kid\":\"2011-04-29\"}"
|
||||
"]"
|
||||
"}",
|
||||
*lws_jwe_ex_a3_jwk_json = (uint8_t *) /* oct symmetric keys */
|
||||
"{\"keys\":"
|
||||
"["
|
||||
"{\"kty\":\"oct\","
|
||||
"\"alg\":\"A128KW\","
|
||||
"\"k\":\"GawgguFyGrWKav7AX4VKUg\"},"
|
||||
|
||||
"{\"kty\":\"oct\","
|
||||
"\"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75"
|
||||
"aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\","
|
||||
"\"kid\":\"HMAC key used in JWS spec Appendix A.1 example\"}"
|
||||
"]"
|
||||
"}",
|
||||
|
||||
*lws_jwe_ex_b_jwk_json = (uint8_t *) /* x5c example (no parent JSON) */
|
||||
"{\"kty\":\"RSA\","
|
||||
"\"use\":\"sig\","
|
||||
"\"kid\":\"1b94c\","
|
||||
"\"n\":\"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08"
|
||||
"PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q"
|
||||
"u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a"
|
||||
"YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH"
|
||||
"MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv"
|
||||
"VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"x5c\":"
|
||||
"[\"MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB"
|
||||
"gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD"
|
||||
"VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1"
|
||||
"wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg"
|
||||
"NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV"
|
||||
"QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w"
|
||||
"YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH"
|
||||
"YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66"
|
||||
"s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6"
|
||||
"SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn"
|
||||
"fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq"
|
||||
"PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk"
|
||||
"aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA"
|
||||
"QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL"
|
||||
"+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1"
|
||||
"zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL"
|
||||
"2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo"
|
||||
"4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq"
|
||||
"gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==\"]"
|
||||
"}",
|
||||
*lws_jwe_ex_c1_jwk_json = (uint8_t *) /* RSA enc private key (no parent JSON) */
|
||||
"{"
|
||||
"\"kty\":\"RSA\","
|
||||
"\"kid\":\"juliet@capulet.lit\","
|
||||
"\"use\":\"enc\","
|
||||
"\"n\":\"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy"
|
||||
"O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP"
|
||||
"8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0"
|
||||
"Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X"
|
||||
"OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1"
|
||||
"_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"d\":\"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS"
|
||||
"NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U"
|
||||
"vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu"
|
||||
"ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu"
|
||||
"rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a"
|
||||
"hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ\","
|
||||
"\"p\":\"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf"
|
||||
"QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8"
|
||||
"UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws\","
|
||||
"\"q\":\"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I"
|
||||
"edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK"
|
||||
"rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s\","
|
||||
"\"dp\":\"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3"
|
||||
"tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w"
|
||||
"Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c\","
|
||||
"\"dq\":\"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9"
|
||||
"GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy"
|
||||
"mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots\","
|
||||
"\"qi\":\"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq"
|
||||
"abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o"
|
||||
"Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8\""
|
||||
"}" /*,
|
||||
lws_jwe_ex_c1_plaintext[] = {
|
||||
123, 34, 107, 116, 121, 34, 58, 34, 82, 83, 65, 34, 44, 34, 107,
|
||||
105, 100, 34, 58, 34, 106, 117, 108, 105, 101, 116, 64, 99, 97, 112,
|
||||
117, 108, 101, 116, 46, 108, 105, 116, 34, 44, 34, 117, 115, 101, 34,
|
||||
58, 34, 101, 110, 99, 34, 44, 34, 110, 34, 58, 34, 116, 54, 81, 56,
|
||||
80, 87, 83, 105, 49, 100, 107, 74, 106, 57, 104, 84, 80, 56, 104, 78,
|
||||
89, 70, 108, 118, 97, 100, 77, 55, 68, 102, 108, 87, 57, 109, 87,
|
||||
101, 112, 79, 74, 104, 74, 54, 54, 119, 55, 110, 121, 111, 75, 49,
|
||||
103, 80, 78, 113, 70, 77, 83, 81, 82, 121, 79, 49, 50, 53, 71, 112,
|
||||
45, 84, 69, 107, 111, 100, 104, 87, 114, 48, 105, 117, 106, 106, 72,
|
||||
86, 120, 55, 66, 99, 86, 48, 108, 108, 83, 52, 119, 53, 65, 67, 71,
|
||||
103, 80, 114, 99, 65, 100, 54, 90, 99, 83, 82, 48, 45, 73, 113, 111,
|
||||
109, 45, 81, 70, 99, 78, 80, 56, 83, 106, 103, 48, 56, 54, 77, 119,
|
||||
111, 113, 81, 85, 95, 76, 89, 121, 119, 108, 65, 71, 90, 50, 49, 87,
|
||||
83, 100, 83, 95, 80, 69, 82, 121, 71, 70, 105, 78, 110, 106, 51, 81,
|
||||
81, 108, 79, 56, 89, 110, 115, 53, 106, 67, 116, 76, 67, 82, 119, 76,
|
||||
72, 76, 48, 80, 98, 49, 102, 69, 118, 52, 53, 65, 117, 82, 73, 117,
|
||||
85, 102, 86, 99, 80, 121, 83, 66, 87, 89, 110, 68, 121, 71, 120, 118,
|
||||
106, 89, 71, 68, 83, 77, 45, 65, 113, 87, 83, 57, 122, 73, 81, 50,
|
||||
90, 105, 108, 103, 84, 45, 71, 113, 85, 109, 105, 112, 103, 48, 88,
|
||||
79, 67, 48, 67, 99, 50, 48, 114, 103, 76, 101, 50, 121, 109, 76, 72,
|
||||
106, 112, 72, 99, 105, 67, 75, 86, 65, 98, 89, 53, 45, 76, 51, 50,
|
||||
45, 108, 83, 101, 90, 79, 45, 79, 115, 54, 85, 49, 53, 95, 97, 88,
|
||||
114, 107, 57, 71, 119, 56, 99, 80, 85, 97, 88, 49, 95, 73, 56, 115,
|
||||
76, 71, 117, 83, 105, 86, 100, 116, 51, 67, 95, 70, 110, 50, 80, 90,
|
||||
51, 90, 56, 105, 55, 52, 52, 70, 80, 70, 71, 71, 99, 71, 49, 113,
|
||||
115, 50, 87, 122, 45, 81, 34, 44, 34, 101, 34, 58, 34, 65, 81, 65,
|
||||
66, 34, 44, 34, 100, 34, 58, 34, 71, 82, 116, 98, 73, 81, 109, 104,
|
||||
79, 90, 116, 121, 115, 122, 102, 103, 75, 100, 103, 52, 117, 95, 78,
|
||||
45, 82, 95, 109, 90, 71, 85, 95, 57, 107, 55, 74, 81, 95, 106, 110,
|
||||
49, 68, 110, 102, 84, 117, 77, 100, 83, 78, 112, 114, 84, 101, 97,
|
||||
83, 84, 121, 87, 102, 83, 78, 107, 117, 97, 65, 119, 110, 79, 69, 98,
|
||||
73, 81, 86, 121, 49, 73, 81, 98, 87, 86, 86, 50, 53, 78, 89, 51, 121,
|
||||
98, 99, 95, 73, 104, 85, 74, 116, 102, 114, 105, 55, 98, 65, 88, 89,
|
||||
69, 82, 101, 87, 97, 67, 108, 51, 104, 100, 108, 80, 75, 88, 121, 57,
|
||||
85, 118, 113, 80, 89, 71, 82, 48, 107, 73, 88, 84, 81, 82, 113, 110,
|
||||
115, 45, 100, 86, 74, 55, 106, 97, 104, 108, 73, 55, 76, 121, 99,
|
||||
107, 114, 112, 84, 109, 114, 77, 56, 100, 87, 66, 111, 52, 95, 80,
|
||||
77, 97, 101, 110, 78, 110, 80, 105, 81, 103, 79, 48, 120, 110, 117,
|
||||
84, 111, 120, 117, 116, 82, 90, 74, 102, 74, 118, 71, 52, 79, 120,
|
||||
52, 107, 97, 51, 71, 79, 82, 81, 100, 57, 67, 115, 67, 90, 50, 118,
|
||||
115, 85, 68, 109, 115, 88, 79, 102, 85, 69, 78, 79, 121, 77, 113, 65,
|
||||
68, 67, 54, 112, 49, 77, 51, 104, 51, 51, 116, 115, 117, 114, 89, 49,
|
||||
53, 107, 57, 113, 77, 83, 112, 71, 57, 79, 88, 95, 73, 74, 65, 88,
|
||||
109, 120, 122, 65, 104, 95, 116, 87, 105, 90, 79, 119, 107, 50, 75,
|
||||
52, 121, 120, 72, 57, 116, 83, 51, 76, 113, 49, 121, 88, 56, 67, 49,
|
||||
69, 87, 109, 101, 82, 68, 107, 75, 50, 97, 104, 101, 99, 71, 56, 53,
|
||||
45, 111, 76, 75, 81, 116, 53, 86, 69, 112, 87, 72, 75, 109, 106, 79,
|
||||
105, 95, 103, 74, 83, 100, 83, 103, 113, 99, 78, 57, 54, 88, 53, 50,
|
||||
101, 115, 65, 81, 34, 44, 34, 112, 34, 58, 34, 50, 114, 110, 83, 79,
|
||||
86, 52, 104, 75, 83, 78, 56, 115, 83, 52, 67, 103, 99, 81, 72, 70,
|
||||
98, 115, 48, 56, 88, 98, 111, 70, 68, 113, 75, 117, 109, 51, 115, 99,
|
||||
52, 104, 51, 71, 82, 120, 114, 84, 109, 81, 100, 108, 49, 90, 75, 57,
|
||||
117, 119, 45, 80, 73, 72, 102, 81, 80, 48, 70, 107, 120, 88, 86, 114,
|
||||
120, 45, 87, 69, 45, 90, 69, 98, 114, 113, 105, 118, 72, 95, 50, 105,
|
||||
67, 76, 85, 83, 55, 119, 65, 108, 54, 88, 118, 65, 82, 116, 49, 75,
|
||||
107, 73, 97, 85, 120, 80, 80, 83, 89, 66, 57, 121, 107, 51, 49, 115,
|
||||
48, 81, 56, 85, 75, 57, 54, 69, 51, 95, 79, 114, 65, 68, 65, 89, 116,
|
||||
65, 74, 115, 45, 77, 51, 74, 120, 67, 76, 102, 78, 103, 113, 104, 53,
|
||||
54, 72, 68, 110, 69, 84, 84, 81, 104, 72, 51, 114, 67, 84, 53, 84,
|
||||
51, 121, 74, 119, 115, 34, 44, 34, 113, 34, 58, 34, 49, 117, 95, 82,
|
||||
105, 70, 68, 80, 55, 76, 66, 89, 104, 51, 78, 52, 71, 88, 76, 84, 57,
|
||||
79, 112, 83, 75, 89, 80, 48, 117, 81, 90, 121, 105, 97, 90, 119, 66,
|
||||
116, 79, 67, 66, 78, 74, 103, 81, 120, 97, 106, 49, 48, 82, 87, 106,
|
||||
115, 90, 117, 48, 99, 54, 73, 101, 100, 105, 115, 52, 83, 55, 66, 95,
|
||||
99, 111, 83, 75, 66, 48, 75, 106, 57, 80, 97, 80, 97, 66, 122, 103,
|
||||
45, 73, 121, 83, 82, 118, 118, 99, 81, 117, 80, 97, 109, 81, 117, 54,
|
||||
54, 114, 105, 77, 104, 106, 86, 116, 71, 54, 84, 108, 86, 56, 67, 76,
|
||||
67, 89, 75, 114, 89, 108, 53, 50, 122, 105, 113, 75, 48, 69, 95, 121,
|
||||
109, 50, 81, 110, 107, 119, 115, 85, 88, 55, 101, 89, 84, 66, 55, 76,
|
||||
98, 65, 72, 82, 75, 57, 71, 113, 111, 99, 68, 69, 53, 66, 48, 102,
|
||||
56, 48, 56, 73, 52, 115, 34, 44, 34, 100, 112, 34, 58, 34, 75, 107,
|
||||
77, 84, 87, 113, 66, 85, 101, 102, 86, 119, 90, 50, 95, 68, 98, 106,
|
||||
49, 112, 80, 81, 113, 121, 72, 83, 72, 106, 106, 57, 48, 76, 53, 120,
|
||||
95, 77, 79, 122, 113, 89, 65, 74, 77, 99, 76, 77, 90, 116, 98, 85,
|
||||
116, 119, 75, 113, 118, 86, 68, 113, 51, 116, 98, 69, 111, 51, 90,
|
||||
73, 99, 111, 104, 98, 68, 116, 116, 54, 83, 98, 102, 109, 87, 122,
|
||||
103, 103, 97, 98, 112, 81, 120, 78, 120, 117, 66, 112, 111, 79, 79,
|
||||
102, 95, 97, 95, 72, 103, 77, 88, 75, 95, 108, 104, 113, 105, 103,
|
||||
73, 52, 121, 95, 107, 113, 83, 49, 119, 89, 53, 50, 73, 119, 106, 85,
|
||||
110, 53, 114, 103, 82, 114, 74, 45, 121, 89, 111, 49, 104, 52, 49,
|
||||
75, 82, 45, 118, 122, 50, 112, 89, 104, 69, 65, 101, 89, 114, 104,
|
||||
116, 116, 87, 116, 120, 86, 113, 76, 67, 82, 86, 105, 68, 54, 99, 34,
|
||||
44, 34, 100, 113, 34, 58, 34, 65, 118, 102, 83, 48, 45, 103, 82, 120,
|
||||
118, 110, 48, 98, 119, 74, 111, 77, 83, 110, 70, 120, 89, 99, 75, 49,
|
||||
87, 110, 117, 69, 106, 81, 70, 108, 117, 77, 71, 102, 119, 71, 105,
|
||||
116, 81, 66, 87, 116, 102, 90, 49, 69, 114, 55, 116, 49, 120, 68,
|
||||
107, 98, 78, 57, 71, 81, 84, 66, 57, 121, 113, 112, 68, 111, 89, 97,
|
||||
78, 48, 54, 72, 55, 67, 70, 116, 114, 107, 120, 104, 74, 73, 66, 81,
|
||||
97, 106, 54, 110, 107, 70, 53, 75, 75, 83, 51, 84, 81, 116, 81, 53,
|
||||
113, 67, 122, 107, 79, 107, 109, 120, 73, 101, 51, 75, 82, 98, 66,
|
||||
121, 109, 88, 120, 107, 98, 53, 113, 119, 85, 112, 88, 53, 69, 76,
|
||||
68, 53, 120, 70, 99, 54, 70, 101, 105, 97, 102, 87, 89, 89, 54, 51,
|
||||
84, 109, 109, 69, 65, 117, 95, 108, 82, 70, 67, 79, 74, 51, 120, 68,
|
||||
101, 97, 45, 111, 116, 115, 34, 44, 34, 113, 105, 34, 58, 34, 108,
|
||||
83, 81, 105, 45, 119, 57, 67, 112, 121, 85, 82, 101, 77, 69, 114, 80,
|
||||
49, 82, 115, 66, 76, 107, 55, 119, 78, 116, 79, 118, 115, 53, 69, 81,
|
||||
112, 80, 113, 109, 117, 77, 118, 113, 87, 53, 55, 78, 66, 85, 99,
|
||||
122, 83, 99, 69, 111, 80, 119, 109, 85, 113, 113, 97, 98, 117, 57,
|
||||
86, 48, 45, 80, 121, 52, 100, 81, 53, 55, 95, 98, 97, 112, 111, 75,
|
||||
82, 117, 49, 82, 57, 48, 98, 118, 117, 70, 110, 85, 54, 51, 83, 72,
|
||||
87, 69, 70, 103, 108, 90, 81, 118, 74, 68, 77, 101, 65, 118, 109,
|
||||
106, 52, 115, 109, 45, 70, 112, 48, 111, 89, 117, 95, 110, 101, 111,
|
||||
116, 103, 81, 48, 104, 122, 98, 73, 53, 103, 114, 121, 55, 97, 106,
|
||||
100, 89, 121, 57, 45, 50, 108, 78, 120, 95, 55, 54, 97, 66, 90, 111,
|
||||
79, 85, 117, 57, 72, 67, 74, 45, 85, 115, 102, 83, 79, 73, 56, 34,
|
||||
125 } */
|
||||
;
|
||||
|
||||
static int
|
||||
key_import_callback(struct lws_jwk *s, void *user)
|
||||
{
|
||||
lwsl_notice("%s: key type %d\n", __func__, s->kty);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
test_jwk(struct lws_context *context)
|
||||
{
|
||||
struct lws_jwk jwk;
|
||||
|
||||
/* Test 1: A.1: Example public keys */
|
||||
|
||||
if (lws_jwk_import(&jwk, key_import_callback, NULL,
|
||||
(char *)lws_jwe_ex_a1_jwk_json,
|
||||
strlen((char *)lws_jwe_ex_a1_jwk_json)) < 0) {
|
||||
lwsl_notice("Failed to decode JWK test key\n");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_jwk_destroy(&jwk);
|
||||
|
||||
/* Test 1: A.2: Example private keys */
|
||||
|
||||
if (lws_jwk_import(&jwk, key_import_callback, NULL,
|
||||
(char *)lws_jwe_ex_a2_jwk_json,
|
||||
strlen((char *)lws_jwe_ex_a2_jwk_json)) < 0) {
|
||||
lwsl_notice("Failed at A.2\n");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_jwk_destroy(&jwk);
|
||||
|
||||
/* Test 1: A.3: Example symmetric keys */
|
||||
|
||||
if (lws_jwk_import(&jwk, key_import_callback, NULL,
|
||||
(char *)lws_jwe_ex_a3_jwk_json,
|
||||
strlen((char *)lws_jwe_ex_a3_jwk_json)) < 0) {
|
||||
lwsl_notice("Failed at A.3\n");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_jwk_destroy(&jwk);
|
||||
|
||||
/* Test 1: B: Example x509 cert chain (no parent JSON) */
|
||||
|
||||
if (lws_jwk_import(&jwk, NULL, NULL, (char *)lws_jwe_ex_b_jwk_json,
|
||||
strlen((char *)lws_jwe_ex_b_jwk_json)) < 0) {
|
||||
lwsl_notice("Failed at B\n");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_jwk_destroy(&jwk);
|
||||
|
||||
/* Test 1: C.1: Example private key (no parent JSON) */
|
||||
|
||||
if (lws_jwk_import(&jwk, NULL, NULL,
|
||||
(char *)lws_jwe_ex_c1_jwk_json,
|
||||
strlen((char *)lws_jwe_ex_c1_jwk_json)) < 0) {
|
||||
lwsl_notice("Failed at B\n");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_jwk_destroy(&jwk);
|
||||
|
||||
/* end */
|
||||
|
||||
lwsl_notice("%s: selftest OK\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
//bail:
|
||||
// lws_jwk_destroy(&jwk);
|
||||
bail1:
|
||||
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
248
minimal-examples/api-tests/api-test-jose/jws.c
Normal file
248
minimal-examples/api-tests/api-test-jose/jws.c
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* lws-api-test-jose - RFC7515 jws tests
|
||||
*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
* JSON Web Signature is defined in RFC7515
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc7515
|
||||
*
|
||||
* It's basically a way to wrap some JSON with a JSON "header" describing the
|
||||
* crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
|
||||
*
|
||||
* The signature stays with the content, it serves a different purpose than eg
|
||||
* a TLS tunnel to transfer it.
|
||||
*
|
||||
* RFC7518 (JSON Web Algorithms) says for the "alg" names
|
||||
*
|
||||
* | HS256 | HMAC using SHA-256 | Required |
|
||||
* | HS384 | HMAC using SHA-384 | Optional |
|
||||
* | HS512 | HMAC using SHA-512 | Optional |
|
||||
* | RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
|
||||
* | RS384 | RSASSA-PKCS1-v1_5 using | Optional |
|
||||
* | | SHA-384 | |
|
||||
* | RS512 | RSASSA-PKCS1-v1_5 using | Optional |
|
||||
* | | SHA-512 | |
|
||||
* | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
|
||||
* | ES384 | ECDSA using P-384 and SHA-384 | Optional |
|
||||
* | ES512 | ECDSA using P-521 and SHA-512 | Optional |
|
||||
*
|
||||
* Boulder (FOSS ACME provider) supports RS256, ES256, ES384 and ES512
|
||||
* currently. The "Recommended+" just means it is recommended but will likely
|
||||
* be "very recommended" soon.
|
||||
*
|
||||
* We support HS256/384/512 for symmetric crypto, but the choice for the
|
||||
* asymmetric crypto isn't as easy to make.
|
||||
*
|
||||
* Normally you'd choose the EC option but these are defined to use the
|
||||
* "NIST curves" (RFC7518 3.4) which are believed to be insecure.
|
||||
*
|
||||
* https://safecurves.cr.yp.to/
|
||||
*
|
||||
* For that reason we implement RS256/384/512 for asymmetric.
|
||||
*/
|
||||
|
||||
static const char
|
||||
*test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
|
||||
*test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
|
||||
*test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
|
||||
" \"http://example.com/is_root\":true}",
|
||||
*test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
|
||||
"ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
|
||||
*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 =
|
||||
"{\"kty\":\"RSA\","
|
||||
" \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
|
||||
"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
|
||||
"D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
|
||||
"SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
|
||||
"MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
|
||||
"NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
|
||||
"jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
|
||||
"BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
|
||||
"439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
|
||||
"CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
|
||||
"BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
|
||||
"\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
|
||||
"YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
|
||||
"BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
|
||||
"\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
|
||||
"ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
|
||||
"-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
|
||||
"\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
|
||||
"CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
|
||||
"34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
|
||||
"\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
|
||||
"7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
|
||||
"NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
|
||||
"\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
|
||||
"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";
|
||||
|
||||
/*
|
||||
* 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(struct lws_context *context)
|
||||
{
|
||||
struct lws_genhmac_ctx ctx;
|
||||
struct lws_jwk jwk;
|
||||
char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
int n;
|
||||
|
||||
/* Test 1: SHA256 on RFC7515 worked example */
|
||||
|
||||
/* 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_JWK_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[JWK_OCT_KEYEL_K].buf,
|
||||
jwk.e[JWK_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_JWK_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, '.');
|
||||
|
||||
n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
|
||||
p1 + 1, sizeof(buf) - (p1 - buf) - 1,
|
||||
LWS_GENHASH_TYPE_SHA256, &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;
|
||||
}
|
51
minimal-examples/api-tests/api-test-jose/main.c
Normal file
51
minimal-examples/api-tests/api-test-jose/main.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* lws-api-test-jose
|
||||
*
|
||||
* 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>
|
||||
|
||||
int
|
||||
test_jwk(struct lws_context *context);
|
||||
int
|
||||
test_jws(struct lws_context *context);
|
||||
int
|
||||
test_jwe(struct lws_context *context);
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
const char *p;
|
||||
int result = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS JOSE api tests\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;
|
||||
}
|
||||
|
||||
result |= test_jwk(context);
|
||||
result |= test_jws(context);
|
||||
result |= test_jwe(context);
|
||||
|
||||
lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS");
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
return result;
|
||||
}
|
24
minimal-examples/api-tests/api-test-jose/selftest.sh
Executable file
24
minimal-examples/api-tests/api-test-jose/selftest.sh
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# $1: path to minimal example binaries...
|
||||
# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1
|
||||
# that will be ./bin from your build dir
|
||||
#
|
||||
# $2: path for logs and results. The results will go
|
||||
# in a subdir named after the directory this script
|
||||
# is in
|
||||
#
|
||||
# $3: offset for test index count
|
||||
#
|
||||
# $4: total test count
|
||||
#
|
||||
# $5: path to ./minimal-examples dir in lws
|
||||
#
|
||||
# Test return code 0: OK, 254: timed out, other: error indication
|
||||
|
||||
. $5/selftests-library.sh
|
||||
|
||||
COUNT_TESTS=1
|
||||
|
||||
dotest $1 $2 apiselftest
|
||||
exit $FAILS
|
|
@ -435,13 +435,14 @@ lws_acme_load_create_auth_keys(struct per_vhost_data__lws_acme_client *vhd,
|
|||
{
|
||||
int n;
|
||||
|
||||
if (!lws_jwk_load(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH]))
|
||||
if (!lws_jwk_load(&vhd->jwk, vhd->pvop[LWS_TLS_SET_AUTH_PATH],
|
||||
NULL, NULL))
|
||||
return 0;
|
||||
|
||||
strcpy(vhd->jwk.keytype, "RSA");
|
||||
vhd->jwk.kty = LWS_JWK_KYT_RSA;
|
||||
lwsl_notice("Generating ACME %d-bit keypair... "
|
||||
"will take a little while\n", bits);
|
||||
n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, &vhd->jwk.el,
|
||||
n = lws_genrsa_new_keypair(vhd->context, &vhd->rsactx, vhd->jwk.e,
|
||||
bits);
|
||||
if (n) {
|
||||
lwsl_notice("failed to create keypair\n");
|
||||
|
@ -786,7 +787,8 @@ pkt_add_hdrs:
|
|||
ac->replay_nonce,
|
||||
&ac->buf[LWS_PRE],
|
||||
sizeof(ac->buf) -
|
||||
LWS_PRE);
|
||||
LWS_PRE,
|
||||
lws_get_context(wsi));
|
||||
if (ac->len < 0) {
|
||||
ac->len = 0;
|
||||
lwsl_notice("lws_jws_create_packet failed\n");
|
||||
|
|
|
@ -548,7 +548,7 @@ lws_ssh_exec_finish(void *finish_handle, int retcode)
|
|||
static int
|
||||
lws_ssh_parse_plaintext(struct per_session_data__sshd *pss, uint8_t *p, size_t len)
|
||||
{
|
||||
struct lws_genrsa_elements el;
|
||||
struct lws_jwk_elements e[LWS_COUNT_RSA_KEY_ELEMENTS];
|
||||
struct lws_genrsa_ctx ctx;
|
||||
struct lws_ssh_channel *ch;
|
||||
struct lws_subprotocol_scp *scp;
|
||||
|
@ -1247,19 +1247,19 @@ again:
|
|||
* the E and N factors
|
||||
*/
|
||||
|
||||
memset(&el, 0, sizeof(el));
|
||||
memset(e, 0, sizeof(e));
|
||||
pp = pss->ua->pubkey;
|
||||
m = lws_g32(&pp);
|
||||
pp += m;
|
||||
m = lws_g32(&pp);
|
||||
el.e[JWK_KEY_E].buf = pp;
|
||||
el.e[JWK_KEY_E].len = m;
|
||||
e[JWK_RSA_KEYEL_E].buf = pp;
|
||||
e[JWK_RSA_KEYEL_E].len = m;
|
||||
pp += m;
|
||||
m = lws_g32(&pp);
|
||||
el.e[JWK_KEY_N].buf = pp;
|
||||
el.e[JWK_KEY_N].len = m;
|
||||
e[JWK_RSA_KEYEL_N].buf = pp;
|
||||
e[JWK_RSA_KEYEL_N].len = m;
|
||||
|
||||
if (lws_genrsa_create(&ctx, &el))
|
||||
if (lws_genrsa_create(&ctx, e, pss->vhd->context))
|
||||
goto ua_fail;
|
||||
|
||||
/*
|
||||
|
|
|
@ -110,6 +110,7 @@ rm -rf $RPM_BUILD_ROOT
|
|||
"/usr/include/libwebsockets/lws-genhash.h"
|
||||
"/usr/include/libwebsockets/lws-genrsa.h"
|
||||
"/usr/include/libwebsockets/lws-http.h"
|
||||
"/usr/include/libwebsockets/lws-jose.h"
|
||||
"/usr/include/libwebsockets/lws-jwk.h"
|
||||
"/usr/include/libwebsockets/lws-jws.h"
|
||||
"/usr/include/libwebsockets/lws-lejp.h"
|
||||
|
|
Loading…
Add table
Reference in a new issue