2021-12-06 22:03:53 +01:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Wireless daemon for Linux
|
|
|
|
*
|
|
|
|
* Copyright (C) 2021 Intel Corporation. All rights reserved.
|
|
|
|
*
|
|
|
|
* 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; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <ell/ell.h>
|
|
|
|
|
|
|
|
#include "src/dpp-util.h"
|
|
|
|
#include "src/util.h"
|
2021-12-06 22:03:54 +01:00
|
|
|
#include "src/band.h"
|
2021-12-06 22:03:53 +01:00
|
|
|
#include "src/crypto.h"
|
2021-12-16 19:08:51 +01:00
|
|
|
#include "src/json.h"
|
|
|
|
#include "ell/useful.h"
|
2022-01-18 22:25:01 +01:00
|
|
|
#include "ell/asn1-private.h"
|
2021-12-16 19:08:51 +01:00
|
|
|
#include "src/ie.h"
|
2021-12-06 22:03:53 +01:00
|
|
|
|
2023-10-26 22:26:48 +02:00
|
|
|
/* WFA Easy Connect v3.0 C.1 Role-specific Elements for NIST p256 */
|
|
|
|
static const uint8_t dpp_pkex_initiator_p256[64] = {
|
|
|
|
/* X */
|
|
|
|
0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
|
|
|
|
0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
|
|
|
|
0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
|
|
|
|
0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25,
|
|
|
|
/* Y */
|
|
|
|
0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
|
|
|
|
0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
|
|
|
|
0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
|
|
|
|
0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint8_t dpp_pkex_responder_p256[64] = {
|
|
|
|
/* X */
|
|
|
|
0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
|
|
|
|
0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
|
|
|
|
0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
|
|
|
|
0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76,
|
|
|
|
/* Y */
|
|
|
|
0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
|
|
|
|
0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
|
|
|
|
0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
|
|
|
|
0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
|
|
|
|
};
|
|
|
|
|
2021-12-06 22:03:54 +01:00
|
|
|
static void append_freqs(struct l_string *uri,
|
|
|
|
const uint32_t *freqs, size_t len)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
enum band_freq band;
|
|
|
|
|
|
|
|
l_string_append_printf(uri, "C:");
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
uint8_t oper_class;
|
|
|
|
uint8_t channel = band_freq_to_channel(freqs[i], &band);
|
|
|
|
|
|
|
|
/* For now use global operating classes */
|
|
|
|
if (band == BAND_FREQ_2_4_GHZ)
|
|
|
|
oper_class = 81;
|
|
|
|
else
|
|
|
|
oper_class = 115;
|
|
|
|
|
|
|
|
l_string_append_printf(uri, "%u/%u", oper_class, channel);
|
|
|
|
|
|
|
|
if (i != len - 1)
|
|
|
|
l_string_append_c(uri, ',');
|
|
|
|
}
|
|
|
|
|
|
|
|
l_string_append_c(uri, ';');
|
|
|
|
}
|
|
|
|
|
|
|
|
char *dpp_generate_uri(const uint8_t *asn1, size_t asn1_len, uint8_t version,
|
|
|
|
const uint8_t *mac, const uint32_t *freqs,
|
|
|
|
size_t freqs_len, const char *info, const char *host)
|
|
|
|
{
|
|
|
|
struct l_string *uri = l_string_new(256);
|
|
|
|
char *base64;
|
|
|
|
|
|
|
|
base64 = l_base64_encode(asn1, asn1_len, 0);
|
|
|
|
|
|
|
|
l_string_append_printf(uri, "DPP:K:%s;", base64);
|
|
|
|
l_free(base64);
|
|
|
|
|
|
|
|
if (mac)
|
|
|
|
l_string_append_printf(uri, "M:%02x%02x%02x%02x%02x%02x;",
|
|
|
|
MAC_STR(mac));
|
|
|
|
|
|
|
|
if (freqs)
|
|
|
|
append_freqs(uri, freqs, freqs_len);
|
|
|
|
|
|
|
|
if (info)
|
|
|
|
l_string_append_printf(uri, "I:%s;", info);
|
|
|
|
|
|
|
|
if (host)
|
|
|
|
l_string_append_printf(uri, "H:%s;", host);
|
|
|
|
|
|
|
|
if (version)
|
|
|
|
l_string_append_printf(uri, "V:%u;", version);
|
|
|
|
|
|
|
|
l_string_append_c(uri, ';');
|
|
|
|
|
|
|
|
return l_string_unwrap(uri);
|
|
|
|
}
|
|
|
|
|
2021-12-16 19:08:51 +01:00
|
|
|
static uint32_t dpp_parse_akm(char *akms)
|
|
|
|
{
|
|
|
|
_auto_(l_strv_free) char **split = l_strsplit(akms, '+');
|
|
|
|
char **i = split;
|
|
|
|
uint32_t akm_out = 0;
|
|
|
|
|
|
|
|
while (*i) {
|
|
|
|
if (!strncmp(*i, "psk", 3))
|
|
|
|
akm_out |= IE_RSN_AKM_SUITE_PSK;
|
|
|
|
else if (!strncmp(*i, "sae", 3))
|
|
|
|
akm_out |= IE_RSN_AKM_SUITE_SAE_SHA256;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return akm_out;
|
|
|
|
}
|
|
|
|
|
2023-11-17 15:12:24 +01:00
|
|
|
static bool dpp_parse_extra_options(struct dpp_configuration *config,
|
|
|
|
struct json_iter *extra)
|
|
|
|
{
|
|
|
|
struct json_iter host_val;
|
|
|
|
struct json_iter hidden_val;
|
|
|
|
bool hostname = false;
|
|
|
|
bool hidden = false;
|
|
|
|
|
|
|
|
if (!json_iter_parse(extra,
|
|
|
|
JSON_OPTIONAL("send_hostname", JSON_PRIMITIVE,
|
|
|
|
&host_val),
|
|
|
|
JSON_OPTIONAL("hidden", JSON_PRIMITIVE, &hidden_val),
|
|
|
|
JSON_UNDEFINED))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The values are optional in order to support backwards compatibility
|
|
|
|
* if more are added, but if the key does exist require the type
|
|
|
|
* matches and fail otherwise.
|
|
|
|
*/
|
|
|
|
if (json_iter_is_valid(&host_val) &&
|
|
|
|
!json_iter_get_boolean(&host_val, &hostname))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (json_iter_is_valid(&hidden_val) &&
|
|
|
|
!json_iter_get_boolean(&hidden_val, &hidden))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
config->send_hostname = hostname;
|
|
|
|
config->hidden = hidden;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-16 19:08:51 +01:00
|
|
|
/*
|
|
|
|
* TODO: This handles the most basic configuration. i.e. a configuration object
|
|
|
|
* with ssid/passphrase/akm.
|
|
|
|
*/
|
|
|
|
struct dpp_configuration *dpp_parse_configuration_object(const char *json,
|
|
|
|
size_t json_len)
|
|
|
|
{
|
|
|
|
struct dpp_configuration *config;
|
|
|
|
struct json_contents *c;
|
|
|
|
struct json_iter iter;
|
|
|
|
struct json_iter discovery;
|
|
|
|
struct json_iter cred;
|
2023-11-17 15:12:24 +01:00
|
|
|
struct json_iter extra;
|
2021-12-16 19:08:51 +01:00
|
|
|
_auto_(l_free) char *tech = NULL;
|
|
|
|
_auto_(l_free) char *ssid = NULL;
|
|
|
|
_auto_(l_free) char *akm = NULL;
|
|
|
|
_auto_(l_free) char *pass = NULL;
|
|
|
|
_auto_(l_free) char *psk = NULL;
|
|
|
|
|
|
|
|
c = json_contents_new(json, json_len);
|
|
|
|
if (!c)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
json_iter_init(&iter, c);
|
|
|
|
|
|
|
|
if (!json_iter_parse(&iter,
|
|
|
|
JSON_MANDATORY("wi-fi_tech", JSON_STRING, &tech),
|
|
|
|
JSON_MANDATORY("discovery", JSON_OBJECT, &discovery),
|
|
|
|
JSON_MANDATORY("cred", JSON_OBJECT, &cred),
|
2023-11-17 15:12:24 +01:00
|
|
|
JSON_OPTIONAL("/net/connman/iwd", JSON_OBJECT, &extra),
|
2021-12-16 19:08:51 +01:00
|
|
|
JSON_UNDEFINED))
|
|
|
|
goto free_contents;
|
|
|
|
|
|
|
|
if (!tech || strncmp(tech, "infra", 5))
|
|
|
|
goto free_contents;
|
|
|
|
|
|
|
|
if (!json_iter_parse(&discovery,
|
|
|
|
JSON_MANDATORY("ssid", JSON_STRING, &ssid),
|
|
|
|
JSON_UNDEFINED))
|
|
|
|
goto free_contents;
|
|
|
|
|
2022-01-11 17:51:11 +01:00
|
|
|
if (!ssid || !util_ssid_is_utf8(strlen(ssid), (const uint8_t *) ssid))
|
2021-12-16 19:08:51 +01:00
|
|
|
goto free_contents;
|
|
|
|
|
|
|
|
if (!json_iter_parse(&cred,
|
|
|
|
JSON_MANDATORY("akm", JSON_STRING, &akm),
|
|
|
|
JSON_OPTIONAL("pass", JSON_STRING, &pass),
|
|
|
|
JSON_OPTIONAL("psk", JSON_STRING, &psk),
|
|
|
|
JSON_UNDEFINED))
|
|
|
|
goto free_contents;
|
|
|
|
|
|
|
|
if (!pass && (!psk || strlen(psk) != 64))
|
|
|
|
goto free_contents;
|
|
|
|
|
|
|
|
config = l_new(struct dpp_configuration, 1);
|
|
|
|
|
|
|
|
if (pass)
|
|
|
|
config->passphrase = l_steal_ptr(pass);
|
|
|
|
else
|
|
|
|
config->psk = l_steal_ptr(psk);
|
|
|
|
|
|
|
|
memcpy(config->ssid, ssid, strlen(ssid));
|
|
|
|
config->ssid_len = strlen(ssid);
|
|
|
|
|
|
|
|
config->akm_suites = dpp_parse_akm(akm);
|
|
|
|
if (!config->akm_suites)
|
|
|
|
goto free_config;
|
|
|
|
|
2023-11-17 15:12:24 +01:00
|
|
|
if (json_iter_is_valid(&extra)) {
|
|
|
|
if (!dpp_parse_extra_options(config, &extra))
|
|
|
|
l_warn("Extra settings failed to parse!");
|
|
|
|
}
|
|
|
|
|
2021-12-16 19:08:51 +01:00
|
|
|
json_contents_free(c);
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
|
|
|
free_config:
|
|
|
|
dpp_configuration_free(config);
|
|
|
|
free_contents:
|
|
|
|
json_contents_free(c);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-12-20 22:49:09 +01:00
|
|
|
/*
|
|
|
|
* The DPP spec does not specify a difference between FT AKMs and their normal
|
|
|
|
* counterpart. Because of this any FT AKM will just result in the standard
|
|
|
|
* 'psk' or 'sae' AKM.
|
|
|
|
*/
|
|
|
|
static const char *dpp_akm_to_string(enum ie_rsn_akm_suite akm_suite)
|
|
|
|
{
|
|
|
|
switch (akm_suite) {
|
|
|
|
case IE_RSN_AKM_SUITE_PSK:
|
|
|
|
case IE_RSN_AKM_SUITE_FT_USING_PSK:
|
|
|
|
case IE_RSN_AKM_SUITE_PSK_SHA256:
|
|
|
|
return "psk";
|
|
|
|
case IE_RSN_AKM_SUITE_SAE_SHA256:
|
|
|
|
case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256:
|
|
|
|
return "sae";
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char *dpp_configuration_to_json(struct dpp_configuration *config)
|
|
|
|
{
|
|
|
|
_auto_(l_free) char *pass_or_psk;
|
|
|
|
_auto_(l_free) char *ssid;
|
|
|
|
|
|
|
|
ssid = l_malloc(config->ssid_len + 1);
|
|
|
|
memcpy(ssid, config->ssid, config->ssid_len);
|
|
|
|
ssid[config->ssid_len] = '\0';
|
|
|
|
|
|
|
|
if (config->passphrase)
|
|
|
|
pass_or_psk = l_strdup_printf("\"pass\":\"%s\"",
|
|
|
|
config->passphrase);
|
|
|
|
else
|
|
|
|
pass_or_psk = l_strdup_printf("\"psk\":\"%s\"",
|
|
|
|
config->psk);
|
|
|
|
|
|
|
|
return l_strdup_printf("{\"wi-fi_tech\":\"infra\","
|
2023-11-17 15:12:24 +01:00
|
|
|
"\"discovery\":{"
|
|
|
|
"\"ssid\":\"%s\""
|
|
|
|
"},"
|
|
|
|
"\"cred\":{"
|
|
|
|
"\"akm\":\"%s\",%s"
|
|
|
|
"},"
|
|
|
|
"\"/net/connman/iwd\":{"
|
|
|
|
"\"send_hostname\":%s,"
|
|
|
|
"\"hidden\":%s}"
|
|
|
|
"}",
|
2021-12-20 22:49:09 +01:00
|
|
|
ssid, dpp_akm_to_string(config->akm_suites),
|
2023-11-17 15:12:24 +01:00
|
|
|
pass_or_psk,
|
|
|
|
config->send_hostname ? "true" : "false",
|
|
|
|
config->hidden ? "true" : "false");
|
2021-12-20 22:49:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct dpp_configuration *dpp_configuration_new(
|
|
|
|
const struct l_settings *settings,
|
|
|
|
const char *ssid,
|
|
|
|
enum ie_rsn_akm_suite akm_suite)
|
|
|
|
{
|
|
|
|
struct dpp_configuration *config;
|
|
|
|
_auto_(l_free) char *passphrase = NULL;
|
|
|
|
_auto_(l_free) char *psk = NULL;
|
|
|
|
size_t ssid_len = strlen(ssid);
|
2023-11-17 15:12:24 +01:00
|
|
|
bool send_hostname;
|
|
|
|
bool hidden;
|
2021-12-20 22:49:09 +01:00
|
|
|
|
|
|
|
if (!l_settings_has_group(settings, "Security"))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
passphrase = l_settings_get_string(settings, "Security", "Passphrase");
|
|
|
|
if (!passphrase) {
|
|
|
|
psk = l_settings_get_string(settings, "Security",
|
|
|
|
"PreSharedKey");
|
|
|
|
if (!psk)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
config = l_new(struct dpp_configuration, 1);
|
|
|
|
|
|
|
|
memcpy(config->ssid, ssid, ssid_len);
|
|
|
|
config->ssid[ssid_len] = '\0';
|
|
|
|
config->ssid_len = ssid_len;
|
|
|
|
|
|
|
|
if (passphrase)
|
|
|
|
config->passphrase = l_steal_ptr(passphrase);
|
|
|
|
else
|
|
|
|
config->psk = l_steal_ptr(psk);
|
|
|
|
|
|
|
|
|
|
|
|
config->akm_suites = akm_suite;
|
|
|
|
|
2023-11-17 15:12:24 +01:00
|
|
|
if (!l_settings_get_bool(settings, "IPv4", "SendHostname",
|
|
|
|
&send_hostname))
|
|
|
|
send_hostname = false;
|
|
|
|
|
|
|
|
if (!l_settings_get_bool(settings, "Settings", "Hidden", &hidden))
|
|
|
|
hidden = false;
|
|
|
|
|
|
|
|
config->send_hostname = send_hostname;
|
|
|
|
config->hidden = hidden;
|
|
|
|
|
2021-12-20 22:49:09 +01:00
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
2021-12-16 19:08:51 +01:00
|
|
|
void dpp_configuration_free(struct dpp_configuration *config)
|
|
|
|
{
|
|
|
|
if (config->passphrase)
|
|
|
|
l_free(config->passphrase);
|
|
|
|
|
|
|
|
if (config->psk)
|
|
|
|
l_free(config->psk);
|
|
|
|
|
|
|
|
l_free(config);
|
|
|
|
}
|
|
|
|
|
2021-12-06 22:03:55 +01:00
|
|
|
void dpp_attr_iter_init(struct dpp_attr_iter *iter, const uint8_t *pdu,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
iter->pos = pdu;
|
|
|
|
iter->end = pdu + len;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dpp_attr_iter_next(struct dpp_attr_iter *iter,
|
|
|
|
enum dpp_attribute_type *type_out, size_t *len_out,
|
|
|
|
const uint8_t **data_out)
|
|
|
|
{
|
|
|
|
enum dpp_attribute_type type;
|
2022-01-14 17:32:30 +01:00
|
|
|
uint16_t len;
|
2021-12-06 22:03:55 +01:00
|
|
|
|
|
|
|
if (iter->pos + 4 > iter->end)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
type = l_get_le16(iter->pos);
|
|
|
|
len = l_get_le16(iter->pos + 2);
|
|
|
|
|
|
|
|
iter->pos += 4;
|
|
|
|
|
2022-01-14 17:32:30 +01:00
|
|
|
if (iter->end - iter->pos < len)
|
2021-12-06 22:03:55 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
*type_out = type;
|
|
|
|
*len_out = len;
|
|
|
|
*data_out = iter->pos;
|
|
|
|
|
|
|
|
iter->pos += len;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-17 20:14:48 +01:00
|
|
|
size_t dpp_append_attr(uint8_t *to, enum dpp_attribute_type type,
|
|
|
|
void *attr, size_t attr_len)
|
|
|
|
{
|
|
|
|
l_put_le16(type, to);
|
|
|
|
l_put_le16(attr_len, to + 2);
|
|
|
|
memcpy(to + 4, attr, attr_len);
|
|
|
|
|
|
|
|
return attr_len + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The use of ad0/ad1 differs with different protocol frame types, which is why
|
|
|
|
* this is left up to the caller to pass the correct AD bytes. The usage is
|
|
|
|
* defined in:
|
|
|
|
*
|
|
|
|
* 6.3.1.4 Protocol Conventions (for authentication)
|
|
|
|
* 6.4.1 Overview (for configuration)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
uint8_t *dpp_unwrap_attr(const void *ad0, size_t ad0_len, const void *ad1,
|
|
|
|
size_t ad1_len, const void *key, size_t key_len,
|
|
|
|
const void *wrapped, size_t wrapped_len,
|
|
|
|
size_t *unwrapped_len)
|
|
|
|
{
|
|
|
|
struct iovec ad[2];
|
|
|
|
uint8_t *unwrapped;
|
|
|
|
size_t ad_size = 0;
|
|
|
|
|
|
|
|
if (ad0) {
|
|
|
|
ad[ad_size].iov_base = (void *) ad0;
|
|
|
|
ad[ad_size].iov_len = ad0_len;
|
|
|
|
ad_size++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ad1) {
|
|
|
|
ad[ad_size].iov_base = (void *) ad1;
|
|
|
|
ad[ad_size].iov_len = ad1_len;
|
|
|
|
ad_size++;
|
|
|
|
}
|
|
|
|
|
|
|
|
unwrapped = l_malloc(wrapped_len - 16);
|
|
|
|
|
2021-12-20 22:49:08 +01:00
|
|
|
if (!aes_siv_decrypt(key, key_len, wrapped, wrapped_len, ad, ad_size,
|
2021-12-17 20:14:48 +01:00
|
|
|
unwrapped)) {
|
|
|
|
l_free(unwrapped);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*unwrapped_len = wrapped_len - 16;
|
|
|
|
|
|
|
|
return unwrapped;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encrypt DPP attributes encapsulated in DPP wrapped data.
|
|
|
|
*
|
|
|
|
* ad0/ad0_len - frame specific AD0 component
|
|
|
|
* ad1/ad0_len - frame specific AD1 component
|
|
|
|
* to - buffer to encrypt data.
|
|
|
|
* to_len - size of 'to'
|
|
|
|
* key - key used to encrypt
|
|
|
|
* key_len - size of 'key'
|
|
|
|
* num_attrs - number of attributes listed (type, length, data triplets)
|
|
|
|
* ... - List of attributes, Type, Length, and data
|
|
|
|
*/
|
|
|
|
size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len,
|
|
|
|
const void *ad1, size_t ad1_len,
|
|
|
|
uint8_t *to, size_t to_len,
|
|
|
|
const void *key, size_t key_len,
|
|
|
|
size_t num_attrs, ...)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
size_t attrs_len = 0;
|
|
|
|
_auto_(l_free) uint8_t *plaintext = NULL;
|
|
|
|
uint8_t *ptr;
|
|
|
|
struct iovec ad[2];
|
|
|
|
size_t ad_size = 0;
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, num_attrs);
|
|
|
|
|
|
|
|
/* Count up total attributes length */
|
|
|
|
for (i = 0; i < num_attrs; i++) {
|
|
|
|
va_arg(va, enum dpp_attribute_type);
|
|
|
|
attrs_len += va_arg(va, size_t) + 4;
|
|
|
|
va_arg(va, void*);
|
|
|
|
}
|
|
|
|
|
2022-03-18 17:44:16 +01:00
|
|
|
va_end(va);
|
|
|
|
|
2021-12-17 20:14:48 +01:00
|
|
|
if (to_len < attrs_len + 4 + 16)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
plaintext = l_malloc(attrs_len);
|
|
|
|
|
|
|
|
ptr = plaintext;
|
|
|
|
|
|
|
|
va_start(va, num_attrs);
|
|
|
|
|
|
|
|
/* Build up plaintext attributes */
|
|
|
|
for (i = 0; i < num_attrs; i++) {
|
|
|
|
enum dpp_attribute_type type = va_arg(va,
|
|
|
|
enum dpp_attribute_type);
|
|
|
|
size_t l = va_arg(va, size_t);
|
|
|
|
void *p = va_arg(va, void *);
|
|
|
|
|
|
|
|
l_put_le16(type, ptr);
|
|
|
|
ptr += 2;
|
|
|
|
l_put_le16(l, ptr);
|
|
|
|
ptr += 2;
|
|
|
|
memcpy(ptr, p, l);
|
|
|
|
ptr += l;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
ptr = to;
|
|
|
|
|
|
|
|
l_put_le16(DPP_ATTR_WRAPPED_DATA, ptr);
|
|
|
|
ptr += 2;
|
|
|
|
l_put_le16(attrs_len + 16, ptr);
|
|
|
|
ptr += 2;
|
|
|
|
|
|
|
|
if (ad0) {
|
|
|
|
ad[ad_size].iov_base = (void *) ad0;
|
|
|
|
ad[ad_size].iov_len = ad0_len;
|
|
|
|
ad_size++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ad1) {
|
|
|
|
ad[ad_size].iov_base = (void *) ad1;
|
|
|
|
ad[ad_size].iov_len = ad1_len;
|
|
|
|
ad_size++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aes_siv_encrypt(key, key_len, plaintext, attrs_len,
|
|
|
|
ad, ad_size, ptr))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return attrs_len + 4 + 16;
|
|
|
|
}
|
|
|
|
|
2021-12-06 22:03:53 +01:00
|
|
|
/*
|
|
|
|
* EasyConnect 2.0 Table 3. Key and Nonce Length Dependency on Prime Length
|
|
|
|
*/
|
|
|
|
static enum l_checksum_type dpp_sha_from_key_len(size_t len)
|
|
|
|
{
|
|
|
|
if (len == 32)
|
|
|
|
return L_CHECKSUM_SHA256;
|
|
|
|
else if (len == 48)
|
|
|
|
return L_CHECKSUM_SHA384;
|
|
|
|
else if (len == 64)
|
|
|
|
return L_CHECKSUM_SHA512;
|
|
|
|
else
|
|
|
|
return L_CHECKSUM_NONE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t dpp_nonce_len_from_key_len(size_t len)
|
|
|
|
{
|
|
|
|
if (len == 32)
|
|
|
|
return 16;
|
|
|
|
else if (len == 48)
|
|
|
|
return 24;
|
|
|
|
else if (len == 64)
|
|
|
|
return 32;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 3.2.2
|
|
|
|
* H()
|
|
|
|
*/
|
|
|
|
bool dpp_hash(enum l_checksum_type type, uint8_t *out, unsigned int num, ...)
|
|
|
|
{
|
|
|
|
struct l_checksum *sha = l_checksum_new(type);
|
|
|
|
size_t hsize = l_checksum_digest_length(type);
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, num);
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++) {
|
|
|
|
void *data = va_arg(va, void *);
|
|
|
|
size_t len = va_arg(va, size_t);
|
|
|
|
|
|
|
|
l_checksum_update(sha, data, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
l_checksum_get_digest(sha, out, hsize);
|
|
|
|
l_checksum_free(sha);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 3.2.2
|
|
|
|
*
|
|
|
|
* HKDF is defined as:
|
|
|
|
*
|
|
|
|
* key = HKDF(salt, info, ikm)
|
|
|
|
* = HKDF-Expand(HKDF-Extract(salt, ikm), info, len)
|
|
|
|
*
|
|
|
|
* Note: A NULL 'salt' means a zero'ed buffer and 'salt_len' should still be
|
|
|
|
* set for this zero'ed buffer length.
|
|
|
|
*/
|
|
|
|
static bool dpp_hkdf(enum l_checksum_type sha, const void *salt,
|
|
|
|
size_t salt_len, const char *info, const void *ikm,
|
|
|
|
size_t ikm_len, void *out, size_t out_len)
|
|
|
|
{
|
|
|
|
uint8_t tmp[64];
|
|
|
|
uint8_t zero_salt[64] = { 0 };
|
|
|
|
size_t hash_len = l_checksum_digest_length(sha);
|
|
|
|
|
|
|
|
if (!salt)
|
|
|
|
salt = zero_salt;
|
|
|
|
|
|
|
|
if (!hkdf_extract(sha, salt, salt_len, 1, tmp,
|
|
|
|
ikm, ikm_len))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return hkdf_expand(sha, tmp, hash_len, info, out, out_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dpp_derive_r_auth(const void *i_nonce, const void *r_nonce,
|
|
|
|
size_t nonce_len, struct l_ecc_point *i_proto,
|
|
|
|
struct l_ecc_point *r_proto,
|
2023-10-12 22:01:36 +02:00
|
|
|
struct l_ecc_point *i_boot,
|
2021-12-06 22:03:53 +01:00
|
|
|
struct l_ecc_point *r_boot,
|
|
|
|
void *r_auth)
|
|
|
|
{
|
|
|
|
uint64_t pix[L_ECC_MAX_DIGITS];
|
|
|
|
uint64_t prx[L_ECC_MAX_DIGITS];
|
|
|
|
uint64_t brx[L_ECC_MAX_DIGITS];
|
2023-10-12 22:01:36 +02:00
|
|
|
uint64_t bix[L_ECC_MAX_DIGITS];
|
2021-12-06 22:03:53 +01:00
|
|
|
size_t keys_len;
|
|
|
|
uint8_t zero = 0;
|
|
|
|
enum l_checksum_type type;
|
|
|
|
|
|
|
|
keys_len = l_ecc_point_get_x(i_proto, pix, sizeof(pix));
|
|
|
|
l_ecc_point_get_x(r_proto, prx, sizeof(prx));
|
|
|
|
l_ecc_point_get_x(r_boot, brx, sizeof(brx));
|
|
|
|
|
2023-10-12 22:01:36 +02:00
|
|
|
if (i_boot)
|
|
|
|
l_ecc_point_get_x(i_boot, bix, sizeof(bix));
|
|
|
|
|
2021-12-06 22:03:53 +01:00
|
|
|
type = dpp_sha_from_key_len(keys_len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [ BI.x | ] BR.x | 0)
|
|
|
|
*/
|
2023-10-12 22:01:36 +02:00
|
|
|
return dpp_hash(type, r_auth, 7, i_nonce, nonce_len, r_nonce, nonce_len,
|
|
|
|
pix, keys_len, prx, keys_len,
|
|
|
|
bix, i_boot ? keys_len : 0, brx, keys_len,
|
2022-06-03 19:00:16 +02:00
|
|
|
&zero, (size_t) 1);
|
2021-12-06 22:03:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool dpp_derive_i_auth(const void *r_nonce, const void *i_nonce,
|
|
|
|
size_t nonce_len, struct l_ecc_point *r_proto,
|
|
|
|
struct l_ecc_point *i_proto,
|
2023-10-12 22:01:36 +02:00
|
|
|
struct l_ecc_point *r_boot,
|
|
|
|
struct l_ecc_point *i_boot, void *i_auth)
|
2021-12-06 22:03:53 +01:00
|
|
|
{
|
|
|
|
uint64_t prx[L_ECC_MAX_DIGITS];
|
|
|
|
uint64_t pix[L_ECC_MAX_DIGITS];
|
|
|
|
uint64_t brx[L_ECC_MAX_DIGITS];
|
2023-10-12 22:01:36 +02:00
|
|
|
uint64_t bix[L_ECC_MAX_DIGITS];
|
2021-12-06 22:03:53 +01:00
|
|
|
size_t keys_len;
|
|
|
|
uint8_t one = 1;
|
|
|
|
enum l_checksum_type type;
|
|
|
|
|
|
|
|
keys_len = l_ecc_point_get_x(r_proto, prx, sizeof(prx));
|
|
|
|
l_ecc_point_get_x(i_proto, pix, sizeof(pix));
|
|
|
|
l_ecc_point_get_x(r_boot, brx, sizeof(brx));
|
|
|
|
|
2023-10-12 22:01:36 +02:00
|
|
|
if (i_boot)
|
|
|
|
l_ecc_point_get_x(i_boot, bix, sizeof(bix));
|
|
|
|
|
2021-12-06 22:03:53 +01:00
|
|
|
type = dpp_sha_from_key_len(keys_len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [ BI.x | ] 1)
|
|
|
|
*/
|
2023-10-12 22:01:36 +02:00
|
|
|
return dpp_hash(type, i_auth, 7, r_nonce, nonce_len, i_nonce, nonce_len,
|
2022-06-03 19:00:16 +02:00
|
|
|
prx, keys_len, pix, keys_len, brx, keys_len,
|
2023-10-12 22:01:36 +02:00
|
|
|
bix, i_boot ? keys_len : 0,
|
2022-06-03 19:00:16 +02:00
|
|
|
&one, (size_t) 1);
|
2021-12-06 22:03:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Derives key k1. This returns the intermediate secret M.x used in deriving
|
|
|
|
* key ke.
|
|
|
|
*/
|
|
|
|
struct l_ecc_scalar *dpp_derive_k1(const struct l_ecc_point *i_proto_public,
|
|
|
|
const struct l_ecc_scalar *boot_private,
|
|
|
|
void *k1)
|
|
|
|
{
|
|
|
|
struct l_ecc_scalar *m;
|
|
|
|
uint64_t mx_bytes[L_ECC_MAX_DIGITS];
|
|
|
|
ssize_t key_len;
|
|
|
|
enum l_checksum_type sha;
|
|
|
|
|
|
|
|
if (!l_ecdh_generate_shared_secret(boot_private, i_proto_public, &m))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
key_len = l_ecc_scalar_get_data(m, mx_bytes, sizeof(mx_bytes));
|
2021-12-10 17:48:39 +01:00
|
|
|
if (key_len < 0)
|
|
|
|
goto free_m;
|
2021-12-06 22:03:53 +01:00
|
|
|
|
|
|
|
sha = dpp_sha_from_key_len(key_len);
|
|
|
|
|
|
|
|
if (!dpp_hkdf(sha, NULL, key_len, "first intermediate key", mx_bytes,
|
2021-12-10 17:48:39 +01:00
|
|
|
key_len, k1, key_len))
|
|
|
|
goto free_m;
|
2021-12-06 22:03:53 +01:00
|
|
|
|
|
|
|
return m;
|
2021-12-10 17:48:39 +01:00
|
|
|
|
|
|
|
free_m:
|
|
|
|
l_ecc_scalar_free(m);
|
|
|
|
return NULL;
|
2021-12-06 22:03:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Derives key k2. This returns the intermediate secret N.x used in deriving
|
|
|
|
* key ke.
|
|
|
|
*/
|
|
|
|
struct l_ecc_scalar *dpp_derive_k2(const struct l_ecc_point *i_proto_public,
|
|
|
|
const struct l_ecc_scalar *proto_private,
|
|
|
|
void *k2)
|
|
|
|
{
|
|
|
|
struct l_ecc_scalar *n;
|
|
|
|
uint64_t nx_bytes[L_ECC_MAX_DIGITS];
|
|
|
|
ssize_t key_len;
|
|
|
|
enum l_checksum_type sha;
|
|
|
|
|
|
|
|
if (!l_ecdh_generate_shared_secret(proto_private, i_proto_public, &n))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
key_len = l_ecc_scalar_get_data(n, nx_bytes, sizeof(nx_bytes));
|
2021-12-10 17:48:39 +01:00
|
|
|
if (key_len < 0)
|
|
|
|
goto free_n;
|
2021-12-06 22:03:53 +01:00
|
|
|
|
|
|
|
sha = dpp_sha_from_key_len(key_len);
|
|
|
|
|
|
|
|
if (!dpp_hkdf(sha, NULL, key_len, "second intermediate key", nx_bytes,
|
2021-12-10 17:48:39 +01:00
|
|
|
key_len, k2, key_len))
|
|
|
|
goto free_n;
|
2021-12-06 22:03:53 +01:00
|
|
|
|
|
|
|
return n;
|
2021-12-10 17:48:39 +01:00
|
|
|
|
|
|
|
free_n:
|
|
|
|
l_ecc_scalar_free(n);
|
|
|
|
return NULL;
|
2021-12-06 22:03:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool dpp_derive_ke(const uint8_t *i_nonce, const uint8_t *r_nonce,
|
|
|
|
struct l_ecc_scalar *m, struct l_ecc_scalar *n,
|
2023-10-12 22:01:37 +02:00
|
|
|
struct l_ecc_point *l, void *ke)
|
2021-12-06 22:03:53 +01:00
|
|
|
{
|
|
|
|
uint8_t nonces[32 + 32];
|
|
|
|
size_t nonce_len;
|
|
|
|
uint64_t mx_bytes[L_ECC_MAX_DIGITS];
|
|
|
|
uint64_t nx_bytes[L_ECC_MAX_DIGITS];
|
2023-10-12 22:01:37 +02:00
|
|
|
uint64_t lx_bytes[L_ECC_MAX_DIGITS];
|
2021-12-06 22:03:53 +01:00
|
|
|
uint64_t bk[L_ECC_MAX_DIGITS];
|
|
|
|
ssize_t key_len;
|
|
|
|
enum l_checksum_type sha;
|
|
|
|
|
|
|
|
key_len = l_ecc_scalar_get_data(m, mx_bytes, sizeof(mx_bytes));
|
|
|
|
l_ecc_scalar_get_data(n, nx_bytes, sizeof(nx_bytes));
|
|
|
|
|
|
|
|
nonce_len = dpp_nonce_len_from_key_len(key_len);
|
|
|
|
sha = dpp_sha_from_key_len(key_len);
|
|
|
|
|
2023-10-12 22:01:37 +02:00
|
|
|
if (l)
|
|
|
|
l_ecc_point_get_x(l, lx_bytes, key_len * 2);
|
|
|
|
|
2021-12-06 22:03:53 +01:00
|
|
|
memcpy(nonces, i_nonce, nonce_len);
|
|
|
|
memcpy(nonces + nonce_len, r_nonce, nonce_len);
|
|
|
|
|
|
|
|
/* bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [ | L.x]) */
|
2023-10-12 22:01:37 +02:00
|
|
|
if (!hkdf_extract(sha, nonces, nonce_len * 2, 3, bk, mx_bytes,
|
|
|
|
key_len, nx_bytes, key_len, lx_bytes, l ? key_len : 0))
|
2021-12-06 22:03:53 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* ke = HKDF-Expand(bk, "DPP Key", length) */
|
|
|
|
return hkdf_expand(sha, bk, key_len, "DPP Key", ke, key_len);
|
|
|
|
}
|
2021-12-16 19:08:45 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Values derived from OID definitions in https://www.secg.org/sec2-v2.pdf
|
|
|
|
* Appendix A.2.1
|
|
|
|
*
|
|
|
|
* 1.2.840.10045.2.1 (ecPublicKey)
|
|
|
|
*/
|
2022-01-18 22:25:01 +01:00
|
|
|
static struct asn1_oid ec_oid = {
|
|
|
|
.asn1_len = 7,
|
|
|
|
.asn1 = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 }
|
|
|
|
};
|
2021-12-16 19:08:45 +01:00
|
|
|
|
|
|
|
/* 1.2.840.10045.3.1.7 (prime256v1) */
|
2022-01-18 22:25:01 +01:00
|
|
|
static struct asn1_oid ec_p256_oid = {
|
|
|
|
.asn1_len = 8,
|
|
|
|
.asn1 = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 }
|
|
|
|
};
|
|
|
|
|
2021-12-16 19:08:45 +01:00
|
|
|
/* 1.3.132.0.34 (secp384r1) */
|
2022-01-18 22:25:01 +01:00
|
|
|
static struct asn1_oid ec_p384_oid = {
|
|
|
|
.asn1_len = 5,
|
|
|
|
.asn1 = { 0x2B, 0x81, 0x04, 0x00, 0x22 }
|
|
|
|
};
|
2021-12-16 19:08:45 +01:00
|
|
|
|
|
|
|
uint8_t *dpp_point_to_asn1(const struct l_ecc_point *p, size_t *len_out)
|
|
|
|
{
|
|
|
|
uint8_t *asn1;
|
|
|
|
uint8_t *ptr;
|
2022-01-18 22:25:01 +01:00
|
|
|
struct asn1_oid *key_type;
|
2021-12-16 19:08:45 +01:00
|
|
|
const struct l_ecc_curve *curve = l_ecc_point_get_curve(p);
|
|
|
|
ssize_t key_size = l_ecc_curve_get_scalar_bytes(curve);
|
|
|
|
uint64_t x[L_ECC_MAX_DIGITS];
|
|
|
|
ssize_t ret;
|
|
|
|
size_t len;
|
2022-01-18 20:59:50 +01:00
|
|
|
uint8_t point_type;
|
2021-12-16 19:08:45 +01:00
|
|
|
|
|
|
|
switch (key_size) {
|
|
|
|
case 32:
|
2022-01-18 22:25:01 +01:00
|
|
|
key_type = &ec_p256_oid;
|
2021-12-16 19:08:45 +01:00
|
|
|
break;
|
|
|
|
case 48:
|
2022-01-18 22:25:01 +01:00
|
|
|
key_type = &ec_p384_oid;
|
2021-12-16 19:08:45 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = l_ecc_point_get_x(p, x, sizeof(x));
|
|
|
|
if (ret < 0 || ret != key_size)
|
|
|
|
return NULL;
|
|
|
|
|
2022-01-18 22:25:01 +01:00
|
|
|
len = 2 + ec_oid.asn1_len + 2 + key_type->asn1_len + 2 + key_size + 4;
|
2021-12-16 19:08:45 +01:00
|
|
|
|
2022-01-18 20:59:50 +01:00
|
|
|
/*
|
|
|
|
* Set the type to whatever avoids doing p - y when reading in the
|
2023-10-10 15:57:02 +02:00
|
|
|
* key. Working backwards from l_ecc_point_from_data if Y is even and
|
|
|
|
* the type is BIT0 there is no subtraction. Similarly if Y is odd
|
2022-01-18 20:59:50 +01:00
|
|
|
* and the type is BIT1.
|
|
|
|
*/
|
2023-10-10 15:57:02 +02:00
|
|
|
if (!l_ecc_point_y_isodd(p))
|
2022-01-18 20:59:50 +01:00
|
|
|
point_type = L_ECC_POINT_TYPE_COMPRESSED_BIT0;
|
|
|
|
else
|
|
|
|
point_type = L_ECC_POINT_TYPE_COMPRESSED_BIT1;
|
|
|
|
|
2021-12-16 19:08:45 +01:00
|
|
|
if (L_WARN_ON(len > 128))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
asn1 = l_malloc(len + 2);
|
|
|
|
ptr = asn1;
|
|
|
|
|
|
|
|
*ptr++ = ASN1_ID_SEQUENCE;
|
|
|
|
/* Length of both OIDs and key, plus tag/len bytes */
|
|
|
|
*ptr++ = len;
|
|
|
|
|
|
|
|
*ptr++ = ASN1_ID_SEQUENCE;
|
|
|
|
|
2022-01-18 22:25:01 +01:00
|
|
|
len = ec_oid.asn1_len + key_type->asn1_len + 4;
|
2021-12-16 19:08:45 +01:00
|
|
|
|
|
|
|
*ptr++ = len;
|
|
|
|
|
|
|
|
*ptr++ = ASN1_ID_OID;
|
2022-01-18 22:25:01 +01:00
|
|
|
*ptr++ = ec_oid.asn1_len;
|
|
|
|
memcpy(ptr, ec_oid.asn1, ec_oid.asn1_len);
|
|
|
|
ptr += ec_oid.asn1_len;
|
2021-12-16 19:08:45 +01:00
|
|
|
|
|
|
|
*ptr++ = ASN1_ID_OID;
|
2022-01-18 22:25:01 +01:00
|
|
|
*ptr++ = key_type->asn1_len;
|
|
|
|
memcpy(ptr, key_type->asn1, key_type->asn1_len);
|
|
|
|
ptr += key_type->asn1_len;
|
2021-12-16 19:08:45 +01:00
|
|
|
|
|
|
|
*ptr++ = ASN1_ID_BIT_STRING;
|
|
|
|
*ptr++ = key_size + 2;
|
|
|
|
*ptr++ = 0x00;
|
2022-01-18 20:59:50 +01:00
|
|
|
*ptr++ = point_type;
|
2021-12-16 19:08:45 +01:00
|
|
|
memcpy(ptr, x, key_size);
|
|
|
|
ptr += key_size;
|
|
|
|
|
|
|
|
if (len_out)
|
|
|
|
*len_out = ptr - asn1;
|
|
|
|
|
|
|
|
return asn1;
|
|
|
|
}
|
2022-01-21 00:44:06 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Only checking for the ASN.1 form:
|
|
|
|
*
|
|
|
|
* SEQUENCE {
|
|
|
|
* SEQUENCE {
|
|
|
|
* OBJECT IDENTIFIER ecPublicKey
|
|
|
|
* OBJECT IDENTIFIER key type (p256/p384)
|
|
|
|
* }
|
|
|
|
* BITSTRING (key data)
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
struct l_ecc_point *dpp_point_from_asn1(const uint8_t *asn1, size_t len)
|
|
|
|
{
|
|
|
|
|
|
|
|
const uint8_t *outer_seq;
|
|
|
|
size_t outer_len;
|
|
|
|
const uint8_t *inner_seq;
|
|
|
|
size_t inner_len;
|
|
|
|
const uint8_t *elem;
|
|
|
|
const uint8_t *key_data;
|
|
|
|
size_t elen = 0;
|
|
|
|
uint8_t tag;
|
|
|
|
unsigned int curve_num;
|
|
|
|
const struct l_ecc_curve *curve;
|
|
|
|
|
|
|
|
/* SEQUENCE */
|
|
|
|
outer_seq = asn1_der_find_elem(asn1, len, 0, &tag, &outer_len);
|
|
|
|
if (!outer_seq || tag != ASN1_ID_SEQUENCE)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* SEQUENCE */
|
2022-02-14 22:48:00 +01:00
|
|
|
inner_seq = asn1_der_find_elem(outer_seq, outer_len, 0, &tag,
|
|
|
|
&inner_len);
|
2022-01-21 00:44:06 +01:00
|
|
|
if (!inner_seq || tag != ASN1_ID_SEQUENCE)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* OBJECT IDENTIFIER (ecPublicKey) */
|
|
|
|
elem = asn1_der_find_elem(inner_seq, inner_len, 0, &tag, &elen);
|
|
|
|
if (!elem || tag != ASN1_ID_OID)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Check that this OID is ecPublicKey */
|
|
|
|
if (!asn1_oid_eq(&ec_oid, elen, elem))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
elem = asn1_der_find_elem(inner_seq, inner_len, 1, &tag, &elen);
|
|
|
|
if (!elem || tag != ASN1_ID_OID)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Check if ELL supports this curve */
|
|
|
|
if (asn1_oid_eq(&ec_p256_oid, elen, elem))
|
|
|
|
curve_num = 19;
|
|
|
|
else if (asn1_oid_eq(&ec_p384_oid, elen, elem))
|
|
|
|
curve_num = 20;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
curve = l_ecc_curve_from_ike_group(curve_num);
|
|
|
|
if (!curve)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* BITSTRING */
|
|
|
|
key_data = asn1_der_find_elem(outer_seq, outer_len, 1, &tag, &elen);
|
2022-02-14 22:48:00 +01:00
|
|
|
if (!key_data || tag != ASN1_ID_BIT_STRING || elen < 2)
|
2022-01-21 00:44:06 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return l_ecc_point_from_data(curve, key_data[1],
|
|
|
|
key_data + 2, elen - 2);
|
|
|
|
}
|
2022-02-22 23:28:39 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Advances 'p' to the next character 'sep' plus one. strchr can be trusted to
|
|
|
|
* find the next character, but we do need to check that the next character + 1
|
|
|
|
* isn't the NULL terminator, i.e. that data actually exists past this point.
|
|
|
|
*/
|
|
|
|
#define TOKEN_NEXT(p, sep) \
|
|
|
|
({ \
|
|
|
|
const char *_next = strchr((p), (sep)); \
|
|
|
|
if (_next) { \
|
|
|
|
if (*(_next + 1) == '\0') \
|
|
|
|
_next = NULL; \
|
|
|
|
else \
|
|
|
|
_next++; \
|
|
|
|
} \
|
|
|
|
_next; \
|
|
|
|
})
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finds the length of the current token (characters until next 'sep'). If no
|
|
|
|
* 'sep' is found zero is returned.
|
|
|
|
*/
|
|
|
|
#define TOKEN_LEN(p, sep) \
|
|
|
|
({ \
|
|
|
|
const char *_next = strchr((p), (sep)); \
|
|
|
|
if (!_next) \
|
|
|
|
_next = (p); \
|
|
|
|
(_next - (p)); \
|
|
|
|
})
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensures 'p' points to something resembling a single character followed by
|
|
|
|
* ':' followed by at least one non-null byte of data. This allows the parse
|
|
|
|
* loop to safely advance the pointer to each tokens data (pos + 2)
|
|
|
|
*/
|
|
|
|
#define TOKEN_OK(p) \
|
|
|
|
((p) && (p)[0] != '\0' && (p)[1] == ':' && (p)[2] != '\0') \
|
|
|
|
|
|
|
|
static struct scan_freq_set *dpp_parse_class_and_channel(const char *token,
|
|
|
|
unsigned int len)
|
|
|
|
{
|
|
|
|
const char *pos = token;
|
|
|
|
char *end;
|
|
|
|
struct scan_freq_set *freqs = scan_freq_set_new();
|
|
|
|
|
|
|
|
/* Checking for <operclass>/<channel>,<operclass>/<channel>,... */
|
|
|
|
for (; pos && pos < token + len; pos = TOKEN_NEXT(pos, ',')) {
|
|
|
|
unsigned long r;
|
|
|
|
uint8_t channel;
|
|
|
|
uint8_t oper_class;
|
|
|
|
uint32_t freq;
|
|
|
|
|
|
|
|
/* strtoul accepts minus and plus signs before value */
|
|
|
|
if (*pos == '-' || *pos == '+')
|
|
|
|
goto free_set;
|
|
|
|
|
|
|
|
/* to check uint8_t overflow */
|
|
|
|
errno = 0;
|
|
|
|
r = oper_class = strtoul(pos, &end, 10);
|
|
|
|
|
|
|
|
if (errno == ERANGE || errno == EINVAL)
|
|
|
|
goto free_set;
|
|
|
|
/*
|
|
|
|
* Did strtoul not advance pointer, not reach the next
|
|
|
|
* token, or overflow?
|
|
|
|
*/
|
|
|
|
if (end == pos || *end != '/' || r != oper_class)
|
|
|
|
goto free_set;
|
|
|
|
|
|
|
|
pos = end + 1;
|
|
|
|
|
|
|
|
if (*pos == '-' || *pos == '+')
|
|
|
|
goto free_set;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
r = channel = strtoul(pos, &end, 10);
|
|
|
|
|
|
|
|
if (errno == ERANGE || errno == EINVAL)
|
|
|
|
goto free_set;
|
|
|
|
/*
|
|
|
|
* Same verification as above, but also checks either for
|
|
|
|
* another pair (,) or end of this token (;)
|
|
|
|
*/
|
|
|
|
if (end == pos || (*end != ',' && *end != ';') || r != channel)
|
|
|
|
goto free_set;
|
|
|
|
|
|
|
|
freq = oci_to_frequency(oper_class, channel);
|
|
|
|
if (!freq)
|
|
|
|
goto free_set;
|
|
|
|
|
|
|
|
scan_freq_set_add(freqs, freq);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token + len != end)
|
|
|
|
goto free_set;
|
|
|
|
|
|
|
|
if (scan_freq_set_isempty(freqs)) {
|
|
|
|
free_set:
|
|
|
|
scan_freq_set_free(freqs);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return freqs;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dpp_parse_mac(const char *str, unsigned int len, uint8_t *mac_out)
|
|
|
|
{
|
|
|
|
uint8_t mac[6];
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (len != 12)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (i = 0; i < 12; i += 2) {
|
|
|
|
if (!l_ascii_isxdigit(str[i]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!l_ascii_isxdigit(str[i + 1]))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sscanf(str, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
|
|
|
|
&mac[0], &mac[1], &mac[2],
|
|
|
|
&mac[3], &mac[4], &mac[5]) != 6)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!util_is_valid_sta_address(mac))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memcpy(mac_out, mac, 6);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dpp_parse_version(const char *str, unsigned int len,
|
|
|
|
uint8_t *version_out)
|
|
|
|
{
|
|
|
|
if (len != 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (str[0] != '1' && str[0] != '2')
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
*version_out = str[0] - '0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct l_ecc_point *dpp_parse_key(const char *str, unsigned int len)
|
|
|
|
{
|
|
|
|
_auto_(l_free) uint8_t *decoded = NULL;
|
|
|
|
size_t decoded_len;
|
|
|
|
|
|
|
|
decoded = l_base64_decode(str, len, &decoded_len);
|
|
|
|
if (!decoded)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return dpp_point_from_asn1(decoded, decoded_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse a bootstrapping URI. This parses the tokens defined in the Easy Connect
|
|
|
|
* spec, and verifies they are the correct syntax. Some values have extra
|
|
|
|
* verification:
|
|
|
|
* - The bootstrapping key is base64 decoded and converted to an l_ecc_point
|
|
|
|
* - The operating class and channels are checked against the OCI table.
|
|
|
|
* - The version is checked to be either 1 or 2, as defined by the spec.
|
|
|
|
* - The MAC is verified to be a valid station address.
|
|
|
|
*/
|
|
|
|
struct dpp_uri_info *dpp_parse_uri(const char *uri)
|
|
|
|
{
|
|
|
|
struct dpp_uri_info *info;
|
|
|
|
const char *pos = uri;
|
|
|
|
const char *end = uri + strlen(uri) - 1;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!l_str_has_prefix(pos, "DPP:"))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
info = l_new(struct dpp_uri_info, 1);
|
|
|
|
|
|
|
|
pos += 4;
|
|
|
|
|
|
|
|
/* EasyConnect 5.2.1 - Bootstrapping information format */
|
|
|
|
for (; TOKEN_OK(pos); pos = TOKEN_NEXT(pos, ';')) {
|
|
|
|
unsigned int len = TOKEN_LEN(pos + 2, ';');
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
goto free_info;
|
|
|
|
|
|
|
|
switch (*pos) {
|
|
|
|
case 'C':
|
|
|
|
info->freqs = dpp_parse_class_and_channel(pos + 2, len);
|
|
|
|
if (!info->freqs)
|
|
|
|
goto free_info;
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
ret = dpp_parse_mac(pos + 2, len, info->mac);
|
|
|
|
if (ret < 0)
|
|
|
|
goto free_info;
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
ret = dpp_parse_version(pos + 2, len, &info->version);
|
|
|
|
if (ret < 0)
|
|
|
|
goto free_info;
|
|
|
|
break;
|
|
|
|
case 'K':
|
|
|
|
info->boot_public = dpp_parse_key(pos + 2, len);
|
|
|
|
if (!info->boot_public)
|
|
|
|
goto free_info;
|
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
case 'I':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto free_info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extra data found after last token */
|
|
|
|
if (pos != end)
|
|
|
|
goto free_info;
|
|
|
|
|
|
|
|
/* The public bootstrapping key is the only required token */
|
|
|
|
if (!info->boot_public)
|
|
|
|
goto free_info;
|
|
|
|
|
|
|
|
return info;
|
|
|
|
|
|
|
|
free_info:
|
|
|
|
dpp_free_uri_info(info);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dpp_free_uri_info(struct dpp_uri_info *info)
|
|
|
|
{
|
|
|
|
if (info->freqs)
|
|
|
|
scan_freq_set_free(info->freqs);
|
|
|
|
|
|
|
|
if (info->boot_public)
|
|
|
|
l_ecc_point_free(info->boot_public);
|
|
|
|
|
|
|
|
if (info->information)
|
|
|
|
l_free(info->information);
|
|
|
|
|
|
|
|
if (info->host)
|
|
|
|
l_free(info->host);
|
|
|
|
|
|
|
|
l_free(info);
|
|
|
|
}
|
2023-10-26 22:26:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 6.3.4 DPP Authentication Confirm
|
|
|
|
*
|
|
|
|
* L = bI * (BR + PR)
|
|
|
|
*/
|
|
|
|
struct l_ecc_point *dpp_derive_li(const struct l_ecc_point *boot_public,
|
|
|
|
const struct l_ecc_point *proto_public,
|
|
|
|
const struct l_ecc_scalar *boot_private)
|
|
|
|
{
|
|
|
|
const struct l_ecc_curve *curve = l_ecc_point_get_curve(boot_public);
|
|
|
|
struct l_ecc_point *ret = l_ecc_point_new(curve);
|
|
|
|
|
|
|
|
l_ecc_point_add(ret, boot_public, proto_public);
|
|
|
|
l_ecc_point_multiply(ret, boot_private, ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 6.3.3 DPP Authentication Response
|
|
|
|
*
|
|
|
|
* L = ((bR + pR) modulo q) * BI
|
|
|
|
*/
|
|
|
|
struct l_ecc_point *dpp_derive_lr(const struct l_ecc_scalar *boot_private,
|
|
|
|
const struct l_ecc_scalar *proto_private,
|
|
|
|
const struct l_ecc_point *peer_public)
|
|
|
|
{
|
|
|
|
const struct l_ecc_curve *curve = l_ecc_point_get_curve(peer_public);
|
|
|
|
_auto_(l_ecc_scalar_free) struct l_ecc_scalar *order =
|
|
|
|
l_ecc_curve_get_order(curve);
|
|
|
|
_auto_(l_ecc_scalar_free) struct l_ecc_scalar *sum =
|
|
|
|
l_ecc_scalar_new(curve, NULL, 0);
|
|
|
|
_auto_(l_ecc_point_free) struct l_ecc_point *ret =
|
|
|
|
l_ecc_point_new(curve);
|
|
|
|
|
|
|
|
if (!l_ecc_scalar_add(sum, boot_private, proto_private, order))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!l_ecc_point_multiply(ret, sum, peer_public))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return l_steal_ptr(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct l_ecc_point *dpp_derive_q(const struct l_ecc_curve *curve,
|
|
|
|
const uint8_t *p_data,
|
|
|
|
const char *key,
|
|
|
|
const char *identifier,
|
|
|
|
const uint8_t *mac)
|
|
|
|
{
|
|
|
|
_auto_(l_ecc_scalar_free) struct l_ecc_scalar *scalar = NULL;
|
|
|
|
_auto_(l_ecc_point_free) struct l_ecc_point *ret = NULL;
|
|
|
|
uint8_t hash[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
unsigned int bytes = l_ecc_curve_get_scalar_bytes(curve);
|
|
|
|
enum l_checksum_type type = dpp_sha_from_key_len(bytes);
|
|
|
|
_auto_(l_ecc_point_free) struct l_ecc_point *p = NULL;
|
|
|
|
struct l_checksum *sha = l_checksum_new(type);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "If the Initiator indicates PKEX with a Protocol Version of 1,
|
|
|
|
* MAC-Initiator shall be the MAC address of the Initiator and the
|
|
|
|
* Protocol Version shall not be present. Otherwise, MAC-Initiator is
|
|
|
|
* not present"
|
|
|
|
*
|
|
|
|
* (This goes for MAC-Responder as well)
|
|
|
|
*/
|
|
|
|
if (mac)
|
|
|
|
l_checksum_update(sha, mac, 6);
|
|
|
|
|
|
|
|
if (identifier)
|
|
|
|
l_checksum_update(sha, identifier, strlen(identifier));
|
|
|
|
|
|
|
|
l_checksum_update(sha, key, strlen(key));
|
|
|
|
l_checksum_get_digest(sha, hash, bytes);
|
|
|
|
l_checksum_free(sha);
|
|
|
|
|
|
|
|
/* Unlikely but can happen */
|
|
|
|
scalar = l_ecc_scalar_new(curve, hash, bytes);
|
|
|
|
if (!scalar)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
p = l_ecc_point_from_data(curve, L_ECC_POINT_TYPE_FULL,
|
|
|
|
p_data, bytes * 2);
|
|
|
|
if (!p)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = l_ecc_point_new(curve);
|
|
|
|
|
|
|
|
if (!l_ecc_point_multiply(ret, scalar, p))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return l_steal_ptr(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 5.6.2 PKEX Exchange Phase
|
|
|
|
*
|
|
|
|
* Qi = H([MAC-Initiator |] [identifier | ] code) * Pi
|
|
|
|
*/
|
|
|
|
struct l_ecc_point *dpp_derive_qi(const struct l_ecc_curve *curve,
|
|
|
|
const char *key,
|
|
|
|
const char *identifier,
|
|
|
|
const uint8_t *mac_initiator)
|
|
|
|
{
|
|
|
|
return dpp_derive_q(curve, dpp_pkex_initiator_p256, key, identifier,
|
|
|
|
mac_initiator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 5.6.2 PKEX Exchange Phase
|
|
|
|
*
|
|
|
|
* Qr = H([MAC-Responder |] [identifier | ] code) * Pr
|
|
|
|
*/
|
|
|
|
struct l_ecc_point *dpp_derive_qr(const struct l_ecc_curve *curve,
|
|
|
|
const char *key,
|
|
|
|
const char *identifier,
|
|
|
|
const uint8_t *mac_responder)
|
|
|
|
{
|
|
|
|
return dpp_derive_q(curve, dpp_pkex_responder_p256, key, identifier,
|
|
|
|
mac_responder);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 5.6.2 PKEX Exchange Phase
|
|
|
|
*
|
|
|
|
* z = HKDF(<>, info | M.x | N.x | code, K.x)
|
|
|
|
*/
|
|
|
|
bool dpp_derive_z(const uint8_t *mac_i, const uint8_t *mac_r,
|
|
|
|
const struct l_ecc_point *n,
|
|
|
|
const struct l_ecc_point *m,
|
|
|
|
const struct l_ecc_point *k,
|
|
|
|
const char *key,
|
|
|
|
const char *identifier,
|
|
|
|
void *z_out, size_t *z_len)
|
|
|
|
{
|
|
|
|
const struct l_ecc_curve *curve = l_ecc_point_get_curve(n);
|
|
|
|
size_t bytes = l_ecc_curve_get_scalar_bytes(curve);
|
|
|
|
enum l_checksum_type sha = dpp_sha_from_key_len(bytes);
|
|
|
|
uint8_t k_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t m_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t n_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t prk[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
|
|
|
|
l_ecc_point_get_x(k, k_x, sizeof(k_x));
|
|
|
|
l_ecc_point_get_x(m, m_x, sizeof(m_x));
|
|
|
|
l_ecc_point_get_x(n, n_x, sizeof(n_x));
|
|
|
|
|
|
|
|
hkdf_extract(sha, NULL, 0, 1, prk, k_x, bytes);
|
|
|
|
|
|
|
|
/* HKDF-Extract (since it doesn't take non-string arguments)*/
|
|
|
|
prf_plus(sha, prk, bytes, z_out, bytes, 5, mac_i, 6, mac_r, 6, m_x,
|
|
|
|
bytes, n_x, bytes, key, strlen(key));
|
|
|
|
|
|
|
|
*z_len = bytes;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 5.6.3 PKEX Commit-Reveal Phase
|
|
|
|
*
|
|
|
|
* Initiator derivation:
|
|
|
|
* u = HMAC(J.x, [MAC-Initiator |] A.x | Y'.x | X.x )
|
|
|
|
*
|
|
|
|
* Responder derivation:
|
|
|
|
* u' = HMAC(J'.x, [MAC-Initiator |] A'.x | Y.x | X'.x)
|
|
|
|
*/
|
|
|
|
bool dpp_derive_u(const struct l_ecc_point *j,
|
|
|
|
const uint8_t *mac_i,
|
|
|
|
const struct l_ecc_point *a,
|
|
|
|
const struct l_ecc_point *y,
|
|
|
|
const struct l_ecc_point *x,
|
|
|
|
void *u_out, size_t *u_len)
|
|
|
|
{
|
|
|
|
const struct l_ecc_curve *curve = l_ecc_point_get_curve(y);
|
|
|
|
uint8_t j_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t a_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t y_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t x_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
size_t bytes = l_ecc_curve_get_scalar_bytes(curve);
|
|
|
|
enum l_checksum_type sha = dpp_sha_from_key_len(bytes);
|
|
|
|
struct l_checksum *hmac;
|
|
|
|
|
|
|
|
l_ecc_point_get_x(j, j_x, bytes);
|
|
|
|
l_ecc_point_get_x(a, a_x, bytes);
|
|
|
|
l_ecc_point_get_x(y, y_x, bytes);
|
|
|
|
l_ecc_point_get_x(x, x_x, bytes);
|
|
|
|
|
|
|
|
/* u = HMAC(J.x, MAC-Initiator | A.x | Y'.x | X.x)*/
|
|
|
|
hmac = l_checksum_new_hmac(sha, j_x, bytes);
|
|
|
|
l_checksum_update(hmac, mac_i, 6);
|
|
|
|
l_checksum_update(hmac, a_x, bytes);
|
|
|
|
l_checksum_update(hmac, y_x, bytes);
|
|
|
|
l_checksum_update(hmac, x_x, bytes);
|
|
|
|
l_checksum_get_digest(hmac, u_out, bytes);
|
|
|
|
l_checksum_free(hmac);
|
|
|
|
|
|
|
|
*u_len = bytes;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 5.6.3 PKEX Commit-Reveal Phase
|
|
|
|
*
|
|
|
|
* Initiator derivation:
|
|
|
|
* v = HMAC(L.x, [MAC-Responder |] B.x | X'.x |Y.x )
|
|
|
|
*
|
|
|
|
* Responder derivation:
|
|
|
|
* v' = HMAC(L.x, [MAC-Responder |] B'.x | X.x | Y'.x )
|
|
|
|
*/
|
|
|
|
bool dpp_derive_v(const struct l_ecc_point *l, const uint8_t *mac,
|
|
|
|
const struct l_ecc_point *b,
|
|
|
|
const struct l_ecc_point *x,
|
|
|
|
const struct l_ecc_point *y,
|
|
|
|
void *v_out, size_t *v_len)
|
|
|
|
{
|
|
|
|
const struct l_ecc_curve *curve = l_ecc_point_get_curve(l);
|
|
|
|
uint8_t l_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t b_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t x_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t y_x[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
size_t bytes = l_ecc_curve_get_scalar_bytes(curve);
|
|
|
|
enum l_checksum_type sha = dpp_sha_from_key_len(bytes);
|
|
|
|
struct l_checksum *hmac;
|
|
|
|
|
|
|
|
l_ecc_point_get_x(l, l_x, sizeof(l_x));
|
|
|
|
l_ecc_point_get_x(b, b_x, sizeof(b_x));
|
|
|
|
l_ecc_point_get_x(x, x_x, sizeof(x_x));
|
|
|
|
l_ecc_point_get_x(y, y_x, sizeof(y_x));
|
|
|
|
|
|
|
|
hmac = l_checksum_new_hmac(sha, l_x, bytes);
|
|
|
|
|
|
|
|
if (mac)
|
|
|
|
l_checksum_update(hmac, mac, 6);
|
|
|
|
|
|
|
|
l_checksum_update(hmac, b_x, bytes);
|
|
|
|
l_checksum_update(hmac, x_x, bytes);
|
|
|
|
l_checksum_update(hmac, y_x, bytes);
|
|
|
|
l_checksum_get_digest(hmac, v_out, bytes);
|
|
|
|
l_checksum_free(hmac);
|
|
|
|
|
|
|
|
*v_len = bytes;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|