1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

ss: auth: sigv4

Add SS pieces for Sigv4 auth support
This commit is contained in:
Jed Lu 2020-12-29 16:42:25 -08:00 committed by Andy Green
parent a8d6ac8923
commit c82910d30c
21 changed files with 1339 additions and 14 deletions

View file

@ -146,7 +146,7 @@
"platforms": "windows-10/x86_64-amd/msvc, windows-10/x86_64-amd/noptmsvc"
},
"secure-streams-proxy": {
"cmake": "-DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_SECURE_STREAMS_PROXY_API=1 -DLWS_WITH_MINIMAL_EXAMPLES=1",
"cmake": "-DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_SECURE_STREAMS_PROXY_API=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_SECURE_STREAMS_AUTH_SIGV4=1",
"platforms": "not windows-10/x86_64-amd/msvc, netbsd/aarch64BE-bcm2837-a53/gcc"
},
"distro_recommended": { # minimal examples also needed for ctest

View file

@ -169,6 +169,10 @@ if (LWS_WITH_HTTP_BASIC_AUTH)
set(LWS_WITH_HTTP_UNCOMMON_HEADERS 1)
endif()
if (LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
set(LWS_WITH_GENCRYPTO 1)
endif()
if (APPLE)
set(LWS_ROLE_DBUS 0)
endif()

View file

@ -141,6 +141,7 @@ option(LWS_WITH_SECURE_STREAMS_CPP "Secure Streams C++ classes" OFF)
option(LWS_WITH_SECURE_STREAMS_PROXY_API "Secure Streams support to work across processes" OFF)
option(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM "Auth support for api.amazon.com" OFF)
option(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY "Secure Streams Policy is hardcoded only" OFF)
option(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4 "Secure Streams Auth support for AWS Sigv4" OFF)
#
# CTest options

View file

@ -173,6 +173,7 @@
#cmakedefine LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM
#cmakedefine LWS_WITH_SECURE_STREAMS_PROXY_API
#cmakedefine LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY
#cmakedefine LWS_WITH_SECURE_STREAMS_AUTH_SIGV4
#cmakedefine LWS_WITH_SELFTESTS
#cmakedefine LWS_WITH_SEQUENCER
#cmakedefine LWS_WITH_SERVER_STATUS

View file

@ -188,6 +188,7 @@ typedef struct lws_ss_auth {
struct lws_ss_auth *next;
const char *name;
const char *type;
const char *streamtype;
uint8_t blob_index;
} lws_ss_auth_t;
@ -289,6 +290,11 @@ typedef struct lws_ss_policy {
const void *plugins_info[2]; /**< plugin-specific data */
#endif
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
/* directly point to the metadata name, no need to expand */
const char *aws_region;
const char *aws_service;
#endif
/*
* We're either a client connection policy that wants a trust store,
* or we're a server policy that wants a mem cert and key... Hold

View file

@ -731,5 +731,47 @@ lws_ss_get_est_peer_tx_credit(struct lws_ss_handle *h);
LWS_VISIBLE LWS_EXTERN const char *
lws_ss_tag(struct lws_ss_handle *h);
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
/**
* lws_ss_sigv4_set_aws_key() - set aws credential into system blob
*
* \param context: lws_context
* \param idx: the system blob index specified in the policy, currently
* up to 4 blobs.
* \param keyid: aws access keyid
* \param key: aws access key
*
* Return 0 if OK or nonzero if e.g. idx is invalid; system blob heap appending
* fails.
*/
LWS_VISIBLE LWS_EXTERN int
lws_ss_sigv4_set_aws_key(struct lws_context* context, uint8_t idx,
const char * keyid, const char * key);
/**
* lws_aws_filesystem_credentials_helper() - read aws credentials from file
*
* \param path: path to read, ~ at start is converted to $HOME contents if any
* \param kid: eg, "aws_access_key_id"
* \param ak: eg, "aws_secret_access_key"
* \param aws_keyid: pointer to pointer for allocated keyid from credentials file
* \param aws_key: pointer to pointer for allocated key from credentials file
*
* Return 0 if both *aws_keyid and *aws_key allocated from the config file, else
* nonzero, and neither *aws_keyid or *aws_key are allocated.
*
* If *aws_keyid and *aws_key are set, it's the user's responsibility to
* free() them when they are no longer needed.
*/
LWS_VISIBLE LWS_EXTERN int
lws_aws_filesystem_credentials_helper(const char *path, const char *kid,
const char *ak, char **aws_keyid,
char **aws_key);
#endif
///@}

View file

@ -42,6 +42,15 @@ typedef enum {
LWS_SYSBLOB_TYPE_MQTT_USERNAME,
LWS_SYSBLOB_TYPE_MQTT_PASSWORD,
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
/* extend 4 more auth blobs, each has 2 slots */
LWS_SYSBLOB_TYPE_EXT_AUTH1,
LWS_SYSBLOB_TYPE_EXT_AUTH2 = LWS_SYSBLOB_TYPE_EXT_AUTH1 + 2,
LWS_SYSBLOB_TYPE_EXT_AUTH3 = LWS_SYSBLOB_TYPE_EXT_AUTH2 + 2,
LWS_SYSBLOB_TYPE_EXT_AUTH4 = LWS_SYSBLOB_TYPE_EXT_AUTH3 + 2,
LWS_SYSBLOB_TYPE_EXT_AUTH4_1,
#endif
LWS_SYSBLOB_TYPE_COUNT /* ... always last */
} lws_system_blob_item_t;

View file

@ -1037,11 +1037,13 @@ lws_vhost_destroy1(struct lws_vhost *vh)
* swap it to a vhost that has the same
* iface + port, but is not closing.
*/
assert(v->lserv_wsi == NULL);
v->lserv_wsi = vh->lserv_wsi;
lwsl_notice("%s: listen skt from %s to %s\n",
__func__, vh->name, v->name);
__func__, lws_vh_tag(vh),
lws_vh_tag(v));
assert(v->lserv_wsi == NULL);
v->lserv_wsi = vh->lserv_wsi;
if (v->lserv_wsi) {
lws_vhost_unbind_wsi(vh->lserv_wsi);

View file

@ -78,6 +78,13 @@ if (LWS_WITH_CLIENT)
)
endif()
if (LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
list(APPEND SOURCES
secure-streams/system/auth-sigv4/sign.c
)
endif()
if (LWS_WITH_SECURE_STREAMS_CPP)
list(APPEND SOURCES secure-streams/cpp/lss.cxx)

View file

@ -217,18 +217,23 @@ auth token blob indexes.
```
...
"auth": [{"name":"lwa","streamtype":"api_amazon_com_lwa","blob":0}]
"auth": [{"name":"newauth","type":"sigv4", "blob":0}]
...
```
Streams can indicate they depend on a valid auth token from one of these schemes
by using the `"use_auth": "name"` member in the streamtype definition, where name
is, eg, "lwa" in the example above.
is, eg, "sigv4" in the example above. If "use_auth" is not in the streamtype
definition, default auth is lwa if "http_auth_header" is there.
### `auth[].name`
This is the name of the authentication scheme used by other streamtypes
### `auth[].type`
Indicate the auth type, e.g. sigv4
### `auth[].streamtype`
This is the auth streamtype to be used to refresh the authentication token
@ -236,7 +241,8 @@ This is the auth streamtype to be used to refresh the authentication token
### `auth[].blob`
This is the auth blob index the authentication token is stored into and retreived
from
from system blob, currently up to 4 blobs.
### `s`
@ -341,6 +347,12 @@ to validate the remote server cert.
Indicate that the streamtype should use the named auth type from the `auth`
array in the policy
### `aws_region`
Indicate which metadata should be used to set aws region for certain streamtype
### `aws_service`
Indicate which metadata should be used to set aws service for certain streamtype
### `server_cert`
**SERVER ONLY**: subject to change... the name of the x.509 cert that is the
@ -742,7 +754,7 @@ proxy to get to the SS proxy, which then goes out to the internet
### 1 Start the SS proxy
Tell it to listen on lo interface on port 1234
Tell it to listen on lo interface on port 1234
```
$ ./bin/lws-minimal-secure-streams-proxy -p 1234 -i lo

View file

@ -101,8 +101,11 @@ static const char * const lejp_tokens_policy[] = {
"s[].*.mqtt_will_retain",
"s[].*.swake_validity",
"s[].*.use_auth",
"s[].*.aws_region",
"s[].*.aws_service",
"s[].*",
"auth[].name",
"auth[].type",
"auth[].streamtype",
"auth[].blob",
"auth[]",
@ -181,8 +184,11 @@ typedef enum {
LSSPPT_MQTT_WILL_RETAIN,
LSSPPT_SWAKE_VALIDITY,
LSSPPT_USE_AUTH,
LSSPPT_AWS_REGION,
LSSPPT_AWS_SERVICE,
LSSPPT_STREAMTYPES,
LSSPPT_AUTH_NAME,
LSSPPT_AUTH_TYPE,
LSSPPT_AUTH_STREAMTYPE,
LSSPPT_AUTH_BLOB,
LSSPPT_AUTH,
@ -621,7 +627,6 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
case LSSPPT_AUTH_BLOB:
a->curr[LTY_AUTH].a->blob_index = (uint8_t)atoi(ctx->buf);
break;
case LSSPPT_HTTP_EXPECT:
a->curr[LTY_POLICY].p->u.http.resp_expect = (uint16_t)atoi(ctx->buf);
break;
@ -713,6 +718,7 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
}
break;
case LSSPPT_METADATA_ITEM:
pmd = a->curr[LTY_POLICY].p->metadata;
a->curr[LTY_POLICY].p->metadata = lwsac_use_zero(&a->ac,
@ -809,11 +815,23 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
case LSSPPT_AUTH_STREAMTYPE:
pp = (char **)&a->curr[LTY_AUTH].a->streamtype;
goto string2;
case LSSPPT_AUTH_TYPE:
pp = (char **)&a->curr[LTY_AUTH].a->type;
goto string2;
case LSSPPT_HTTP_FAIL_REDIRECT:
a->curr[LTY_POLICY].p->u.http.fail_redirect =
reason == LEJPCB_VAL_TRUE;
break;
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
case LSSPPT_AWS_REGION:
pp = (char **)&a->curr[LTY_POLICY].p->aws_region;
goto string2;
case LSSPPT_AWS_SERVICE:
pp = (char **)&a->curr[LTY_POLICY].p->aws_service;
goto string2;
#endif
#endif
#if defined(LWS_ROLE_WS)

View file

@ -464,6 +464,11 @@ lws_ss_policy_unref_trust_store(struct lws_context *context,
int
lws_ss_sys_cpd(struct lws_context *cx);
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
int lws_ss_apply_sigv4(struct lws *wsi, struct lws_ss_handle *h,
unsigned char **p, unsigned char *end);
#endif
typedef int (* const secstream_protocol_connect_munge_t)(lws_ss_handle_t *h,
char *buf, size_t len, struct lws_client_connect_info *i,
union lws_ss_contemp *ct);

View file

@ -616,6 +616,14 @@ malformed:
if (!h->policy->u.http.blob_header[m])
continue;
/*
* To be backward compatible, default is system-wide LWA auth,
* and "http_auth_header" is for default LWA auth, current users do not
* need any change in their policy.
* If user wants different auth/token, need to specify the "use_auth"
* and will be handled after metadata headers are applied.
*/
if (m == LWSSS_HBI_AUTH &&
h->policy->u.http.auth_preamble)
o = lws_snprintf((char *)buf, sizeof(buf), "%s",
@ -649,9 +657,19 @@ malformed:
if (lws_apply_metadata(h, wsi, buf, p, end))
return -1;
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
if (h->policy->auth && h->policy->auth->type &&
!strcmp(h->policy->auth->type, "sigv4")) {
if (lws_ss_apply_sigv4(wsi, h, p, end))
return -1;
}
#endif
(void)oin;
// if (*p != oin)
// lwsl_hexdump_notice(oin, lws_ptr_diff(*p, oin));
//if (*p != oin)
// lwsl_hexdump_notice(oin, lws_ptr_diff_size_t(*p, oin));
}

View file

@ -0,0 +1,547 @@
/*
* Sigv4 support for Secure Streams
*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2020 Andy Green <andy@warmcat.com>
* securestreams-dev@amazon.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
struct sigv4_header {
const char * name;
const char * value;
};
#define MAX_HEADER_NUM 8
struct sigv4 {
struct sigv4_header headers[MAX_HEADER_NUM];
uint8_t hnum;
char ymd[10]; /*YYYYMMDD*/
const char *timestamp;
const char *payload_hash;
const char *region;
const char *service;
};
static const uint8_t blob_idx[] = {
LWS_SYSBLOB_TYPE_EXT_AUTH1,
LWS_SYSBLOB_TYPE_EXT_AUTH2,
LWS_SYSBLOB_TYPE_EXT_AUTH3,
LWS_SYSBLOB_TYPE_EXT_AUTH4,
};
enum {
LWS_SS_SIGV4_KEYID,
LWS_SS_SIGV4_KEY,
LWS_SS_SIGV4_BLOB_SLOTS
};
static inline int add_header(struct sigv4 *s, const char *name, const char *value)
{
if (s->hnum >= MAX_HEADER_NUM) {
lwsl_err("%s too many sigv4 headers\n", __func__);
return -1;
}
s->headers[s->hnum].name = name;
s->headers[s->hnum].value = value;
s->hnum++;
if (!strncmp(name, "x-amz-content-sha256", strlen("x-amz-content-sha256")))
s->payload_hash = value;
if (!strncmp(name, "x-amz-date", strlen("x-amz-date"))) {
s->timestamp = value;
strncpy(s->ymd, value, 8);
}
return 0;
}
static int
cmp_header(const void * a, const void * b)
{
return strcmp(((struct sigv4_header *)a)->name,
((struct sigv4_header *)b)->name);
}
static int
init_sigv4(struct lws *wsi, struct lws_ss_handle *h, struct sigv4 *s)
{
lws_ss_metadata_t *polmd = h->policy->metadata;
int m = 0;
add_header(s, "host:", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
while (polmd) {
if (polmd->value__may_own_heap &&
((uint8_t *)polmd->value__may_own_heap)[0] &&
h->metadata[m].value__may_own_heap) {
/* consider all headers start with "x-amz-" need to be signed */
if (!strncmp(polmd->value__may_own_heap, "x-amz-",
strlen("x-amz-"))) {
if (add_header(s, polmd->value__may_own_heap,
h->metadata[m].value__may_own_heap))
return -1;
}
}
if (!strcmp(h->metadata[m].name, h->policy->aws_region) &&
h->metadata[m].value__may_own_heap)
s->region = h->metadata[m].value__may_own_heap;
if (!strcmp(h->metadata[m].name, h->policy->aws_service) &&
h->metadata[m].value__may_own_heap)
s->service = h->metadata[m].value__may_own_heap;
m++;
polmd = polmd->next;
}
qsort(s->headers, s->hnum, sizeof(struct sigv4_header), cmp_header);
#if 0
do {
int i;
for (i= 0; i<s->hnum; i++)
lwsl_debug("%s hdr %s %s\n", __func__,
s->headers[i].name, s->headers[i].value);
lwsl_debug("%s service: %s region: %s\n", __func__,
s->service, s->region);
} while(0);
#endif
return 0;
}
static void
bin2hex(uint8_t *in, size_t len, char *out)
{
static const char *hex = "0123456789abcdef";
size_t n;
for (n = 0; n < len; n++) {
*out++ = hex[(in[n] >> 4) & 0xf];
*out++ = hex[in[n] & 15];
}
*out = '\0';
}
static int
sha256hash(uint8_t *data, size_t len, char *out)
{
struct lws_genhash_ctx hash_ctx;
uint8_t hash_bin[32];
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256) ||
lws_genhash_update(&hash_ctx, (void *)data, len) ||
lws_genhash_destroy(&hash_ctx, hash_bin))
{
lws_genhash_destroy(&hash_ctx, NULL);
lwsl_err("%s lws_genhash error \n", __func__);
return -1;
}
bin2hex(hash_bin, sizeof(hash_bin), out);
return 0;
}
static int
hmacsha256(const uint8_t *key, size_t keylen, const uint8_t *txt,
size_t txtlen, uint8_t *digest)
{
struct lws_genhmac_ctx hmacctx;
if (lws_genhmac_init(&hmacctx, LWS_GENHMAC_TYPE_SHA256,
key, keylen))
return -1;
if (lws_genhmac_update(&hmacctx, txt, txtlen)) {
lwsl_err("%s: hmac computation failed\n", __func__);
lws_genhmac_destroy(&hmacctx, NULL);
return -1;
}
if (lws_genhmac_destroy(&hmacctx, digest)) {
lwsl_err("%s: problem destroying hmac\n", __func__);
return -1;
}
return 0;
}
static int
build_sign_string(struct lws *wsi, char *buf, size_t bufsz,
struct lws_ss_handle *h, struct sigv4 *s)
{
char hash[65], *end = &buf[bufsz - 1], *start;
int i;
start = buf;
/*
* build canonical_request and hash it
*/
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s\n%s\n",
h->policy->u.http.method,
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
/* TODO, append query string */
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "\n");
for (i = 0; i < s->hnum; i++) {
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s%s\n",
s->headers[i].name, s->headers[i].value);
}
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "\n");
for (i = 0; i < s->hnum; i++) {
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s",
s->headers[i].name);
buf--; /* remove ':' */
*buf++ = ';';
}
buf--; /* remove the trailing ';' */
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "\n%s",
s->payload_hash);
*buf++ = '\0';
assert(buf <= start + bufsz);
sha256hash((uint8_t *)start, strlen(start), hash);
/*
* build sign string like the following
*
* "AWS4-HMAC-SHA256" + "\n" +
* timeStampISO8601Format + "\n" +
* date.Format(<YYYYMMDD>) + "/" + <region> + "/" + <service> + "/aws4_request" + "\n" +
* Hex(SHA256Hash(<CanonicalRequest>))
*/
buf = start;
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s\n",
"AWS4-HMAC-SHA256");
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s\n",
s->timestamp);
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s/%s/%s/%s\n",
s->ymd, s->region, s->service, "aws4_request");
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s", hash);
*buf++ = '\0';
assert(buf <= start + bufsz);
return 0;
}
/*
* DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
* DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
* DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
* SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
*/
static int
calc_signing_key(struct lws *wsi, struct lws_ss_handle *h,
struct sigv4 *s, uint8_t *sign_key)
{
uint8_t key[128], date_key[32], and_region_key[32],
and_service_key[32], *kb;
lws_system_blob_t *ab;
size_t keylen;
int n;
ab = lws_system_get_blob(wsi->a.context,
blob_idx[h->policy->auth->blob_index],
LWS_SS_SIGV4_KEY);
if (!ab)
return -1;
kb = key;
*kb++ = 'A';
*kb++ = 'W';
*kb++ = 'S';
*kb++ = '4';
keylen = sizeof(key) - 4;
if (lws_system_blob_get_size(ab) > keylen - 1)
return -1;
n = lws_system_blob_get(ab, kb, &keylen, 0);
if (n < 0)
return -1;
kb[keylen] = '\0';
hmacsha256((const uint8_t *)key, strlen((const char *)key),
(const uint8_t *)s->ymd, strlen(s->ymd), date_key);
hmacsha256(date_key, sizeof(date_key), (const uint8_t *)s->region,
strlen(s->region), and_region_key);
hmacsha256(and_region_key, sizeof(and_region_key),
(const uint8_t *)s->service,
strlen(s->service), and_service_key);
hmacsha256(and_service_key, sizeof(and_service_key),
(uint8_t *)"aws4_request",
strlen("aws4_request"), sign_key);
return 0;
}
/* Sample auth string:
*
* 'Authorization: AWS4-HMAC-SHA256 Credential=AKIAVHWASOFE7TJ7ZUQY/20200731/us-west-2/s3/aws4_request,
* SignedHeaders=host;x-amz-content-sha256;x-amz-date, \
* Signature=ad9fb75ff3b46c7990e3e8f090abfdd6c01fd67761a517111694377e20698377'
*/
static int
build_auth_string(struct lws *wsi, char * buf, size_t bufsz,
struct lws_ss_handle *h, struct sigv4 *s,
uint8_t *signature_bin)
{
char *start = buf, *end = &buf[bufsz - 1];
char *c;
lws_system_blob_t *ab;
size_t keyidlen = 128; // max keyid len is 128
int n;
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s",
"AWS4-HMAC-SHA256 ");
ab = lws_system_get_blob(wsi->a.context,
blob_idx[h->policy->auth->blob_index],
LWS_SS_SIGV4_KEYID);
if (!ab)
return -1;
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s",
"Credential=");
n = lws_system_blob_get(ab,(uint8_t *)buf, &keyidlen, 0);
if (n < 0)
return -1;
buf += keyidlen;
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "/%s/%s/%s/%s, ",
s->ymd, s->region, s->service, "aws4_request");
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s",
"SignedHeaders=");
for (n = 0; n < s->hnum; n++) {
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
"%s",s->headers[n].name);
buf--; /* remove ':' */
*buf++ = ';';
}
c = buf - 1;
*c = ','; /* overwrite ';' back to ',' */
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
"%s", " Signature=");
bin2hex(signature_bin, 32, buf);
assert(buf+65 <= start + bufsz);
lwsl_debug("%s %s\n", __func__, start);
return 0;
}
int
lws_ss_apply_sigv4(struct lws *wsi, struct lws_ss_handle *h,
unsigned char **p, unsigned char *end)
{
uint8_t buf[512], sign_key[32], signature_bin[32], *bp;
struct sigv4 s;
memset(&s, 0, sizeof(s));
bp = buf;
init_sigv4(wsi, h, &s);
if (!s.timestamp || !s.payload_hash) {
lwsl_err("%s missing headers\n", __func__);
return -1;
}
if (build_sign_string(wsi, (char *)bp, sizeof(buf), h, &s))
return -1;
if (calc_signing_key(wsi, h, &s, sign_key))
return -1;
hmacsha256(sign_key, sizeof(sign_key), (const uint8_t *)buf,
strlen((const char *)buf), signature_bin);
bp = buf; /* reuse for auth_str */
if (build_auth_string(wsi, (char *)bp, sizeof(buf), h, &s,
signature_bin))
return -1;
if (lws_add_http_header_by_name(wsi,
(const uint8_t *)"Authorization:", buf,
(int)strlen((const char*)buf), p, end))
return -1;
return 0;
}
int
lws_ss_sigv4_set_aws_key(struct lws_context* context, uint8_t idx,
const char * keyid, const char * key)
{
const char * s[] = { keyid, key };
lws_system_blob_t *ab;
int i;
if (idx > LWS_ARRAY_SIZE(blob_idx))
return -1;
for (i = 0; i < LWS_SS_SIGV4_BLOB_SLOTS; i++) {
ab = lws_system_get_blob(context, blob_idx[idx], i);
if (!ab)
return -1;
lws_system_blob_heap_empty(ab);
if (lws_system_blob_heap_append(ab, (const uint8_t *)s[i],
strlen(s[i]))) {
lwsl_err("%s: can't store %d \n", __func__, i);
return -1;
}
}
return 0;
}
#if defined(__linux__) || defined(__APPLE__) || defined(WIN32) || \
defined(__FreeBSD__) || defined(__NetBSD__) || defined(__ANDROID__)
/* ie, if we have filesystem ops */
int
lws_aws_filesystem_credentials_helper(const char *path, const char *kid,
const char *ak, char **aws_keyid,
char **aws_key)
{
char *str = NULL, *val = NULL, *line = NULL, sth[128];
size_t len = sizeof(sth);
const char *home = "";
int i, poff = 0;
ssize_t rd;
FILE *fp;
*aws_keyid = *aws_key = NULL;
if (path[0] == '~') {
home = getenv("HOME");
if (home && strlen(home) > sizeof(sth) - 1) /* coverity */
return -1;
else {
if (!home)
home = "";
poff = 1;
}
}
lws_snprintf(sth, sizeof(sth), "%s%s", home, path + poff);
fp = fopen(sth, "r");
if (!fp) {
lwsl_err("%s can't open '%s'\n", __func__, sth);
return -1;
}
while ((rd = getline(&line, &len, fp)) != -1) {
for (i = 0; i < 2; i++) {
size_t slen;
if (strncmp(line, i ? kid : ak, strlen(i ? kid : ak)))
continue;
str = strchr(line, '=');
if (!str)
continue;
str++;
/* only read the first key for each */
if (*(i ? aws_keyid : aws_key))
continue;
/*
* Trim whitespace from the start and end
*/
slen = (size_t)(rd - lws_ptr_diff(str, line));
while (slen && *str == ' ') {
str++;
slen--;
}
while (slen && (str[slen - 1] == '\r' ||
str[slen - 1] == '\n' ||
str[slen - 1] == ' '))
slen--;
val = malloc(slen + 1);
if (!val)
goto bail;
strncpy(val, str, slen);
val[slen] = '\0';
*(i ? aws_keyid : aws_key) = val;
}
}
bail:
fclose(fp);
if (line)
free(line);
if (!*aws_keyid || !*aws_key) {
if (*aws_keyid) {
free(*aws_keyid);
*aws_keyid = NULL;
}
if (*aws_key) {
free(*aws_key);
*aws_key = NULL;
}
lwsl_err("%s can't find aws credentials! \
please check %s\n", __func__, path);
return -1;
}
lwsl_info("%s: '%s' '%s'\n", __func__, *aws_keyid, *aws_key);
return 0;
}
#endif

View file

@ -177,6 +177,11 @@ static const char *canned_root_token_payload =
"&client_id="
"amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d";
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
static char *aws_keyid = NULL,
*aws_key = NULL;
#endif
static int
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
int current, int target)
@ -203,7 +208,18 @@ app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
strlen(canned_root_token_payload));
break;
case LWS_SYSTATE_OPERATIONAL:
if (current == LWS_SYSTATE_OPERATIONAL)
if (current == LWS_SYSTATE_OPERATIONAL) {
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
if (lws_aws_filesystem_credentials_helper(
"~/.aws/credentials",
"aws_access_key_id",
"aws_secret_access_key",
&aws_keyid, &aws_key))
return -1;
lws_ss_sigv4_set_aws_key(context, 0, aws_keyid, aws_key);
#endif
/*
* At this point we have DHCP, ntp, system auth token
* and we can reasonably create the proxy
@ -213,6 +229,7 @@ app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
__func__);
return -1;
}
}
break;
case LWS_SYSTATE_POLICY_INVALID:
/*
@ -315,6 +332,13 @@ int main(int argc, const char **argv)
bad = 0;
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
if (aws_keyid)
free(aws_keyid);
if (aws_key)
free(aws_key);
#endif
lws_context_destroy(context);
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");

View file

@ -0,0 +1,102 @@
project(lws-minimal-secure-streams-sigv4 C)
cmake_minimum_required(VERSION 2.8.12)
find_package(libwebsockets CONFIG REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
include(CheckCSourceCompiles)
include(LwsCheckRequirements)
set(SAMP lws-minimal-secure-streams-sigv4)
set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4 1 requirements)
if (requirements)
add_executable(${SAMP} ss-s3-main.c ss-s3-ss.c)
find_program(VALGRIND "valgrind")
if (LWS_CTEST_INTERNET_AVAILABLE AND NOT WIN32)
if (VALGRIND)
message("testing via valgrind")
add_test(NAME ss-sigv4 COMMAND
${VALGRIND} --tool=memcheck --leak-check=yes --num-callers=20
$<TARGET_FILE:lws-minimal-secure-streams-sigv4>)
else()
add_test(NAME ss-sigv4 COMMAND lws-minimal-secure-streams-sigv4)
endif()
set_tests_properties(ss-sigv4
PROPERTIES
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams-sigv4
TIMEOUT 20)
if (HAS_LWS_WITH_SECURE_STREAMS_PROXY_API OR LWS_WITH_SECURE_STREAMS_PROXY_API)
#
# Define test dep to bring up and take down the test
# proxy
#
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# uds abstract namespace for linux
set(CTEST_SOCKET_PATH "@ctest-sspsigv4-$ENV{SAI_PROJECT}-$ENV{SAI_OVN}")
else()
# filesystem socket for others
set(CTEST_SOCKET_PATH "/tmp/ctest-sspsigv4-$ENV{SAI_PROJECT}-$ENV{SAI_OVN}")
endif()
add_test(NAME st_ssproxysigv4 COMMAND
${CMAKE_SOURCE_DIR}/scripts/ctest-background.sh
ssproxysigv4 $<TARGET_FILE:lws-minimal-secure-streams-proxy>
-i ${CTEST_SOCKET_PATH} --ignore-sigterm)
set_tests_properties(st_ssproxysigv4 PROPERTIES WORKING_DIRECTORY . FIXTURES_SETUP ssproxysigv4 TIMEOUT 800)
add_test(NAME ki_ssproxysigv4 COMMAND
${CMAKE_SOURCE_DIR}/scripts/ctest-background-kill.sh
ssproxysigv4 $<TARGET_FILE:lws-minimal-secure-streams-proxy>
-i ${CTEST_SOCKET_PATH})
set_tests_properties(ki_ssproxysigv4 PROPERTIES FIXTURES_CLEANUP ssproxysigv4)
#
# the client part that will connect to the proxy
#
if (VALGRIND)
message("testing via valgrind")
add_test(NAME sspc-sigv4 COMMAND
${VALGRIND} --tool=memcheck --leak-check=yes --num-callers=20
$<TARGET_FILE:lws-minimal-secure-streams-sigv4-client> -i +${CTEST_SOCKET_PATH})
else()
add_test(NAME sspc-sigv4 COMMAND lws-minimal-secure-streams-sigv4-client -i +${CTEST_SOCKET_PATH})
endif()
set_tests_properties(sspc-sigv4 PROPERTIES
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams-sigv4
FIXTURES_REQUIRED "ssproxysigv4"
TIMEOUT 40)
endif()
endif()
if (websockets_shared)
target_link_libraries(${SAMP} websockets_shared)
add_dependencies(${SAMP} websockets_shared)
else()
target_link_libraries(${SAMP} websockets)
endif()
if (LWS_WITH_SECURE_STREAMS_PROXY_API)
add_compile_options(-DLWS_SS_USE_SSPC)
add_executable(${SAMP}-client ss-s3-main.c ss-s3-ss.c)
if (websockets_shared)
target_link_libraries(${SAMP}-client websockets_shared)
add_dependencies(${SAMP}-client websockets_shared)
else()
target_link_libraries(${SAMP}-client websockets)
endif()
endif()
endif()

View file

@ -0,0 +1,46 @@
# lws minimal secure streams sigv4
The application put a test file to AWS S3, using sigv4 auth.
It does it using Secure Streams... the streamtype is "s3PutObj", along with main
are in ss-s3-main.c
The handler for state changes and payloads for "s3PutObj" is in ss-s3-ss.c
## metadata
"aws_region" and "aws_service" are configured through metadata. Also, at least
"x-amz-content-sha256:" and ""x-amz-date:" headers need to be in metadata.
## credentials
credentials are read from ~/.aws/credentials, make sure you have valid keyid and
key. One need to call lws_ss_sigv4_set_aws_key() to plug in aws credentials into
Secure Streams and the index need to be match of the "blob_index" in entry of "auth"
the policy. In addition, you need to change the S3 bucket name to your own, as
bucket name is unique globally in S3.
## build
```
$ cmake . && make
```
## usage
```
[2020/12/19 15:25:06:9763] U: LWS minimal secure streams sigv4
[2020/12/19 15:25:07:0768] U: ss_s3_state: LWSSSCS_CREATING, ord 0x0
[2020/12/19 15:25:07:0769] U: ss_s3_state: LWSSSCS_POLL, ord 0x0
[2020/12/19 15:25:07:0770] U: ss_s3_state: LWSSSCS_CONNECTING, ord 0x0
[2020/12/19 15:25:07:2317] U: SS / TX Payload
[2020/12/19 15:25:07:2317] U: SS / TX Payload Total = 1024, Pos = 0
[2020/12/19 15:25:07:3267] U: ss_s3_state: LWSSSCS_CONNECTED, ord 0x0
[2020/12/19 15:25:07:3267] U: ss_s3_state: LWSSSCS_QOS_ACK_REMOTE, ord 0x0
[2020/12/19 15:25:07:3267] U: ss_s3_state: LWSSSCS_DISCONNECTED, ord 0x0
[2020/12/19 15:25:07:3268] U: ss_s3_state: LWSSSCS_DESTROYING, ord 0x0
[2020/12/19 15:25:07:3269] U: Completed: OK
```

View file

@ -0,0 +1,245 @@
/*
* S3 Put Object via Secure Streams minimal sigv4 example
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
* Amit Pachore <apachor@amazon.com>
* securestreams-dev@amazon.com
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*/
#include <libwebsockets.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include "ss-s3-put.h"
int interrupted, bad = 1;
static lws_state_notify_link_t nl;
extern const lws_ss_info_t s3_ssi;
#if !defined(LWS_SS_USE_SSPC)
static const char * const default_ss_policy =
"{"
"\"release\":" "\"01234567\","
"\"product\":" "\"myproduct\","
"\"schema-version\":" "1,"
"\"retry\": [" /* named backoff / retry strategies */
"{\"default\": {"
"\"backoff\": [" "100,"
"200,"
"300,"
"500,"
"1000"
"],"
"\"conceal\":" "5,"
"\"jitterpc\":" "20,"
"\"svalidping\":" "30,"
"\"svalidhup\":" "35"
"}}"
"],"
"\"certs\": [" /* named individual certificates in BASE64 DER */
"{\"baltimore_cybertrust_root\": \"" /* LE X3 signed by ISRG X1 root */
"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ"
"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD"
"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX"
"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y"
"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy"
"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr"
"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr"
"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK"
"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu"
"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy"
"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye"
"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1"
"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3"
"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92"
"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx"
"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0"
"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz"
"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS"
"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp"
"\"}"
"],"
"\"trust_stores\": [" /* named cert chains */
"{"
"\"name\": \"s3-root-cert\","
"\"stack\": ["
"\"baltimore_cybertrust_root\""
"]"
"}"
"],"
"\"auth\": [" /* named cert chains */
"{"
"\"name\": \"sigv4_brahms\","
"\"type\": \"sigv4\","
"\"blob\": 0"
"}"
"],"
"\"s\": ["
"{\"s3PutObj\": {"
"\"endpoint\":" "\"${s3bucket}.s3.amazonaws.com\","
"\"port\":" "443,"
"\"protocol\":" "\"h1\","
"\"http_method\":" "\"PUT\","
"\"http_url\":" "\"${s3Obj}\","
"\"http_no_content_length\": false,"
"\"tls\":" "true,"
"\"tls_trust_store\":" "\"s3-root-cert\","
"\"opportunistic\":" "true,"
"\"retry\":" "\"default\","
"\"use_auth\":" "\"sigv4_brahms\","
"\"aws_region\":" "\"region\","
"\"aws_service\":" "\"service\","
"\"metadata\": ["
"{\"region\": \"\"},"
"{\"service\": \"\"},"
"{\"s3bucket\": \"\"},"
"{\"s3Obj\": \"\"},"
"{\"ctype\": \"content-type:\"},"
"{\"xcsha256\": \"x-amz-content-sha256:\"},"
"{\"xdate\": \"x-amz-date:\"},"
"{\"xacl\": \"x-amz-acl:\"}"
"]"
"}}"
"]"
"}"
;
static char *aws_keyid, *aws_key;
#endif
static int
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
int current, int target)
{
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
struct lws_ss_handle *h;
switch (target) {
case LWS_SYSTATE_REGISTERED:
break;
case LWS_SYSTATE_OPERATIONAL:
if (current != LWS_SYSTATE_OPERATIONAL)
break;
#if !defined(LWS_SS_USE_SSPC)
if (lws_aws_filesystem_credentials_helper(
"~/.aws/credentials",
"aws_access_key_id",
"aws_secret_access_key",
&aws_keyid, &aws_key))
return -1;
lws_ss_sigv4_set_aws_key(context, 0, aws_keyid, aws_key);
#endif
if (lws_ss_create(context, 0, &s3_ssi, NULL, &h,
NULL, NULL)) {
lwsl_err("%s: failed to create secure stream\n",
__func__);
return -1;
}
break;
}
return 0;
}
static lws_state_notify_link_t * const app_notifier_list[] = {
&nl, NULL
};
static void
sigint_handler(int sig)
{
interrupted = 1;
}
int main(int argc, const char **argv)
{
int logs = LLL_USER | LLL_ERR | LLL_WARN /* | LLL_NOTICE */ ;
struct lws_context_creation_info info;
struct lws_context *context;
int n = 0;
signal(SIGINT, sigint_handler);
lws_set_log_level(logs, NULL);
memset(&info, 0, sizeof info);
lws_cmdline_option_handle_builtin(argc, argv, &info);
lwsl_user("LWS minimal secure streams sigv4 \n");
info.fd_limit_per_thread = 1 + 6 + 1;
info.port = CONTEXT_PORT_NO_LISTEN;
#if defined(LWS_SS_USE_SSPC)
info.protocols = lws_sspc_protocols;
{
const char *p;
/* connect to ssproxy via UDS by default, else via
* tcp connection to this port */
if ((p = lws_cmdline_option(argc, argv, "-p")))
info.ss_proxy_port = (uint16_t)atoi(p);
/* UDS "proxy.ss.lws" in abstract namespace, else this socket
* path; when -p given this can specify the network interface
* to bind to */
if ((p = lws_cmdline_option(argc, argv, "-i")))
info.ss_proxy_bind = p;
/* if -p given, -a specifies the proxy address to connect to */
if ((p = lws_cmdline_option(argc, argv, "-a")))
info.ss_proxy_address = p;
}
#else
info.pss_policies_json = default_ss_policy;
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
#endif
/* integrate us with lws system state management when context created */
nl.name = "app";
nl.notify_cb = app_system_state_nf;
info.register_notifier_list = app_notifier_list;
/* create the context */
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
lws_system_blob_heap_append(lws_system_get_blob(context,
LWS_SYSBLOB_TYPE_DEVICE_TYPE, 0),
(const uint8_t *)"beerfountain", 12);
/* the event loop */
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);
#if !defined(LWS_SS_USE_SSPC)
if (aws_key)
free(aws_key);
if (aws_keyid)
free(aws_keyid);
#endif
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
return bad;
}

View file

@ -0,0 +1,21 @@
/*
* S3 Put Object via Secure Streams minimal sigv4 example
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
* Amit Pachore <apachor@amazon.com>
* securestreams-dev@amazon.com
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*/
typedef struct ss_s3_put {
struct lws_ss_handle *ss;
void *opaque_data;
/* ... application specific state ... */
size_t total;
size_t pos;
uint8_t *buf;
} ss_s3_put_t;

View file

@ -0,0 +1,215 @@
/*
* S3 Put Object via Secure Streams minimal siv4 example
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
* Amit Pachore <apachor@amazon.com>
* securestreams-dev@amazon.com
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*/
#include <libwebsockets.h>
#include <assert.h>
#include "ss-s3-put.h"
extern int interrupted, bad;
static lws_ss_state_return_t
ss_s3_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
{
// ss_s3_put_t *m = (ss_s3_put_t *)userobj;
if (flags & LWSSS_FLAG_EOM) {
bad = 0;
interrupted = 1; /* this example wants to exit after rx */
return LWSSSSRET_DESTROY_ME;
}
lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
lwsl_hexdump_err(buf, len);
return LWSSSSRET_OK;
}
static lws_ss_state_return_t
ss_s3_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
int *flags)
{
ss_s3_put_t *m = (ss_s3_put_t *)userobj;
if (!m->pos)
*flags |= LWSSS_FLAG_SOM;
lwsl_user("%s: Send... total: %ld, pos: %ld\n", __func__,
(long)m->total, (long)m->pos);
if (*len > m->total - m->pos)
*len = m->total - m->pos;
if (!*len)
return LWSSSSRET_TX_DONT_SEND;
memcpy(buf, m->buf + m->pos, *len);
m->pos += *len;
if (m->pos == m->total) {
*flags |= LWSSS_FLAG_EOM;
// m->pos = 0; /* we only want to send once */
} else
lws_ss_request_tx(m->ss);
return LWSSSSRET_OK;
}
static const char *awsService = "s3",
*awsRegion = "us-west-2",
*s3bucketName = "sstest2020",
*s3ObjName = "SSs3upload2.txt";
static char timestamp[32], payload_hash[65];
static uint8_t jpl[1 * 1024];
static void
create_payload(uint8_t *buf, size_t s)
{
int i;
for (i = 0; i < (int)s; i++)
buf[i] = (uint8_t)('a' + i % 16);
}
static void set_time(char *t)
{
/*20150830T123600Z*/
time_t rawtime;
struct tm *info;
time(&rawtime );
info = gmtime(&rawtime);
strftime(t ,20,"%Y%m%dT%H%M%SZ", info);
return;
}
static void bin2hex(uint8_t *in, size_t len, char *out)
{
static const char *hex = "0123456789abcdef";
size_t n;
for (n = 0; n < len; n++) {
*out++ = hex[(in[n] >> 4) & 0xf];
*out++ = hex[in[n] & 15];
}
*out = '\0';
}
static void sigv4_sha256hash_payload(uint8_t *payload, size_t len, char *hash)
{
struct lws_genhash_ctx hash_ctx;
uint8_t hash_bin[32];
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256) ||
/*
* If there is no payload, you must provide the hash of an
* empty string...
*/
lws_genhash_update(&hash_ctx,
payload ? (void *)payload : (void *)"",
payload ? len : 0u) ||
lws_genhash_destroy(&hash_ctx, hash_bin))
{
lws_genhash_destroy(&hash_ctx, NULL);
lwsl_err("%s lws_genhash failed\n", __func__);
return;
}
bin2hex(hash_bin, 32, hash);
}
static lws_ss_state_return_t
ss_s3_state(void *userobj, void *sh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack)
{
ss_s3_put_t *m = (ss_s3_put_t *)userobj;
lwsl_user("%s: %s %s, ord 0x%x\n", __func__, lws_ss_tag(m->ss),
lws_ss_state_name((int)state), (unsigned int)ack);
switch (state) {
case LWSSSCS_CREATING:
lws_ss_set_metadata(m->ss, "s3bucket",
s3bucketName, strlen(s3bucketName));
lws_ss_set_metadata(m->ss, "s3Obj",
s3ObjName, strlen(s3ObjName));
lws_ss_set_metadata(m->ss, "ctype",
"text/plain", strlen("text/plain"));
create_payload(jpl, sizeof(jpl));
m->buf = (uint8_t *)jpl;
m->total = sizeof(jpl);
lws_ss_set_metadata(m->ss, "region",
awsRegion, strlen(awsRegion));
lws_ss_set_metadata(m->ss, "service",
awsService, strlen(awsService));
lws_ss_set_metadata(m->ss, "xacl",
"bucket-owner-full-control",
strlen("bucket-owner-full-control"));
sigv4_sha256hash_payload(m->buf, m->total, payload_hash);
lws_ss_set_metadata(m->ss, "xcsha256",
payload_hash, strlen(payload_hash));
memset(timestamp, 0, sizeof(timestamp));
set_time(timestamp);
lws_ss_set_metadata(m->ss, "xdate",
timestamp, strlen(timestamp));
lws_ss_request_tx_len(m->ss, m->total);
break;
case LWSSSCS_CONNECTED:
lws_ss_request_tx(m->ss);
break;
case LWSSSCS_DISCONNECTED:
return LWSSSSRET_DESTROY_ME;
case LWSSSCS_ALL_RETRIES_FAILED:
/* if we're out of retries, we want to close the app and FAIL */
bad = 1;
return LWSSSSRET_DESTROY_ME;
case LWSSSCS_QOS_ACK_REMOTE:
bad = 0;
break;
case LWSSSCS_QOS_NACK_REMOTE:
bad = 1;
break;
case LWSSSCS_DESTROYING:
interrupted = 1;
break;
default:
break;
}
return 0;
}
const lws_ss_info_t s3_ssi = {
.handle_offset = offsetof(ss_s3_put_t, ss),
.opaque_user_data_offset = offsetof(ss_s3_put_t, opaque_data),
.rx = ss_s3_rx,
.tx = ss_s3_tx,
.state = ss_s3_state,
.user_alloc = sizeof(ss_s3_put_t),
.streamtype = "s3PutObj"
};

View file

@ -68,7 +68,7 @@ if (requirements)
message("testing via valgrind")
add_test(NAME sspc-minimal COMMAND
${VALGRIND} --tool=memcheck --leak-check=yes --num-callers=20
$<TARGET_FILE:lws-minimal-secure-streams> -i +${CTEST_SOCKET_PATH})
$<TARGET_FILE:lws-minimal-secure-streams-client> -i +${CTEST_SOCKET_PATH})
else()
add_test(NAME sspc-minimal COMMAND lws-minimal-secure-streams-client -i +${CTEST_SOCKET_PATH})
endif()