7#include <botan/spake2p.h>
9#include <botan/exceptn.h>
10#include <botan/hash.h>
14#include <botan/mem_ops.h>
15#include <botan/pwdhash.h>
16#include <botan/internal/buffer_stuffer.h>
17#include <botan/internal/concat_util.h>
18#include <botan/internal/fmt.h>
19#include <botan/internal/loadstor.h>
20#include <botan/internal/mem_utils.h>
26std::array<uint8_t, 8> le64_length(std::span<const uint8_t> data) {
27 return store_le(
static_cast<uint64_t
>(data.size()));
31 std::string_view password,
32 std::span<const uint8_t> prover_id,
33 std::span<const uint8_t> verifier_id,
34 std::span<const uint8_t> salt) {
47 auto append_with_le64_length = [&](std::span<const uint8_t> data) {
48 stuffer.
append(le64_length(data));
53 append_with_le64_length(prover_id);
54 append_with_le64_length(verifier_id);
75 pwhash->hash(w0s_w1s, pbkdf_input_sv, salt);
80 return {std::move(w0), std::move(w1)};
85 std::vector<uint8_t> confirm_p;
86 std::vector<uint8_t> confirm_v;
90 std::span<const uint8_t> context,
91 std::span<const uint8_t> prover_id,
92 std::span<const uint8_t> verifier_id,
93 std::span<const uint8_t> share_p,
94 std::span<const uint8_t> share_v,
100 auto hash_with_le64_length = [&](std::span<const uint8_t> data) {
101 hash->update(le64_length(data));
119 hash_with_le64_length(context);
120 hash_with_le64_length(prover_id);
121 hash_with_le64_length(verifier_id);
124 hash_with_le64_length(share_p);
125 hash_with_le64_length(share_v);
130 const auto k_main = hash->final();
145 const size_t mac_key_len = hash->output_length();
146 const auto confirm_keys = kdf->derive_key<
secure_vector<uint8_t>>(2 * mac_key_len, k_main,
"",
"ConfirmationKeys");
149 keys.shared_key = kdf->derive_key<
secure_vector<uint8_t>>(hash->output_length(), k_main,
"",
"SharedKey");
151 mac->set_key(std::span{confirm_keys}.first(mac_key_len));
152 mac->update(share_v);
153 keys.confirm_p = mac->final_stdvec();
155 mac->set_key(std::span{confirm_keys}.last(mac_key_len));
156 mac->update(share_p);
157 keys.confirm_v = mac->final_stdvec();
162std::tuple<EC_Group, EC_AffinePoint, EC_AffinePoint> spake2p_group_params(std::string_view group_name,
163 std::string_view m_hex,
164 std::string_view n_hex) {
168 return {std::move(group), std::move(m), std::move(n)};
173constexpr std::string_view SPAKE2P_P256_M =
"02886e2f97ace46e55ba9dd7242579f2993b64e16ef3dcab95afd497333d8fa12f";
175constexpr std::string_view SPAKE2P_P256_N =
"03d8bbd6c639c62937b04d997f38c3770719c629d7014d49a24b4f98baa1292b49";
177constexpr std::string_view SPAKE2P_P384_M =
178 "030ff0895ae5ebf6187080a82d82b42e2765e3b2f8749c7e05eba366434b363d3dc36f15314739074d2eb8613fceec2853";
180constexpr std::string_view SPAKE2P_P384_N =
181 "02c72cf2e390853a1c1c4ad816a62fd15824f56078918f43f922ca21518f9c543bb252c5490214cf9aa3f0baab4b665c10";
183constexpr std::string_view SPAKE2P_P521_M =
184 "02003f06f38131b2ba2600791e82488e8d20ab889af753a41806c5db18d37d85608cfae06b82e4a72cd744c719193562a653ea1f119ee"
185 "f9356907edc9b56979962d7aa";
187constexpr std::string_view SPAKE2P_P521_N =
188 "0200c7924b9ec017f3094562894336a53c50167ba8c5963876880542bc669e494b2532d76c5b53dfb349fdf69154b9e0048c58a42e8ed"
189 "04cef052a3bc349d95575cd25";
194 m_group(std::move(group)), m_spake2p_m(std::move(m)), m_spake2p_n(std::move(n)), m_hash_fn(hash_fn) {}
197 auto [
group, m, n] = spake2p_group_params(
"secp256r1", SPAKE2P_P256_M, SPAKE2P_P256_N);
198 return SystemParameters(std::move(
group), std::move(m), std::move(n),
"SHA-256");
202 auto [
group, m, n] = spake2p_group_params(
"secp256r1", SPAKE2P_P256_M, SPAKE2P_P256_N);
203 return SystemParameters(std::move(
group), std::move(m), std::move(n),
"SHA-512");
207 auto [
group, m, n] = spake2p_group_params(
"secp384r1", SPAKE2P_P384_M, SPAKE2P_P384_N);
208 return SystemParameters(std::move(
group), std::move(m), std::move(n),
"SHA-256");
212 auto [
group, m, n] = spake2p_group_params(
"secp384r1", SPAKE2P_P384_M, SPAKE2P_P384_N);
213 return SystemParameters(std::move(
group), std::move(m), std::move(n),
"SHA-512");
217 auto [
group, m, n] = spake2p_group_params(
"secp521r1", SPAKE2P_P521_M, SPAKE2P_P521_N);
218 return SystemParameters(std::move(
group), std::move(m), std::move(n),
"SHA-512");
222 std::span<const uint8_t> seed,
223 std::string_view hash_fn) {
224 BOTAN_ARG_CHECK(
group.has_cofactor() ==
false,
"SPAKE2+ is not supported for groups with a cofactor");
226 if(!
group.hash_to_curve_supported(hash_fn)) {
227 throw Not_Implemented(
"SPAKE2+ custom params require hash2curve support which is not available for this curve");
233 return SystemParameters(
group, std::move(m), std::move(n), hash_fn);
237 return 1 + 2 * m_group.get_p_bytes();
241 if(m_hash_fn ==
"SHA-256") {
243 }
else if(m_hash_fn ==
"SHA-384") {
245 }
else if(m_hash_fn ==
"SHA-512") {
253 std::string_view password,
254 std::span<const uint8_t> prover_id,
255 std::span<const uint8_t> verifier_id,
256 std::span<const uint8_t> salt,
265 if(record.size() != scalar_len + point_len) {
266 throw Decoding_Error(
"Invalid length for SPAKE2+ registration record");
276 return RegistrationRecord(std::move(*w0), std::move(*l));
284 std::string_view password,
285 std::span<const uint8_t> prover_id,
286 std::span<const uint8_t> verifier_id,
287 std::span<const uint8_t> salt) {
288 auto [w0, w1] = derive_w0_w1(params, password, prover_id, verifier_id, salt);
289 return ProverSecret(std::move(w0), std::move(w1));
293 return ProverSecret(std::move(w0), std::move(w1));
298 return ProverSecret(std::move(w0_w1->first), std::move(w0_w1->second));
315 std::span<const uint8_t> prover_id,
316 std::span<const uint8_t> verifier_id,
317 std::span<const uint8_t> context) :
320 m_prover_id(prover_id.begin(), prover_id.end()),
321 m_verifier_id(verifier_id.begin(), verifier_id.end()),
322 m_context(context.begin(), context.end()) {}
332 m_our_message = std::make_pair(share_p->serialize_uncompressed(), x);
333 m_state = State::ShareGenerated;
334 return m_our_message->first;
336 throw Internal_Error(
"Computed the identity element during SPAKE2+ key exchange");
343 const size_t share_size = m_params.share_size();
344 const size_t confirm_size = m_params.confirmation_size();
346 if(peer_message.size() != share_size + confirm_size) {
347 throw Decoding_Error(
"Invalid length for SPAKE2+ verifier message");
350 const auto share_v = peer_message.first(share_size);
351 const auto confirm_v = peer_message.last(confirm_size);
358 const auto& w0 = m_secret.m_w0;
359 const auto& w1 = m_secret.m_w1;
360 const auto& n = m_params.spake2p_n();
361 const auto& x = m_our_message->second;
372 spake2p_key_schedule(m_params, m_context, m_prover_id, m_verifier_id, m_our_message->first, share_v, *z, *v, w0);
375 m_our_message.reset();
376 m_state = State::Failed;
380 m_shared_secret = std::move(keys.shared_key);
381 m_our_message.reset();
382 m_state = State::Complete;
384 return keys.confirm_p;
389 return m_shared_secret;
394 std::span<const uint8_t> prover_id,
395 std::span<const uint8_t> verifier_id,
396 std::span<const uint8_t> context) :
399 m_prover_id(prover_id.begin(), prover_id.end()),
400 m_verifier_id(verifier_id.begin(), verifier_id.end()),
401 m_context(context.begin(), context.end()) {}
412 const auto& w0 = m_record.m_w0;
420 throw Internal_Error(
"Computed the identity element during SPAKE2+ key exchange");
422 const auto share_v = share_v_pt->serialize_uncompressed();
429 const auto v = m_record.m_l.mul(y, rng);
431 auto keys = spake2p_key_schedule(m_params, m_context, m_prover_id, m_verifier_id, peer_message, share_v, *z, v, w0);
433 m_shared_secret = std::move(keys.shared_key);
434 m_expected_confirmation = std::move(keys.confirm_p);
435 m_state = State::Responded;
444 m_expected_confirmation.clear();
445 m_shared_secret.clear();
446 m_state = State::Failed;
450 m_expected_confirmation.clear();
451 m_state = State::Complete;
457 m_expected_confirmation.clear();
458 m_state = State::Complete;
463 return m_shared_secret;
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_STATE_CHECK(expr)
#define BOTAN_ARG_CHECK(expr, msg)
Helper class to ease in-place marshalling of concatenated fixed-length values.
constexpr void append(std::span< const uint8_t > buffer)
constexpr bool full() const
static EC_AffinePoint hash_to_curve_ro(const EC_Group &group, std::string_view hash_fn, std::span< const uint8_t > input, std::span< const uint8_t > domain_sep)
static std::optional< EC_AffinePoint > deserialize_uncompressed(const EC_Group &group, std::span< const uint8_t > bytes)
static std::optional< EC_AffinePoint > mul_px_qy(const EC_AffinePoint &p, const EC_Scalar &x, const EC_AffinePoint &q, const EC_Scalar &y, RandomNumberGenerator &rng)
static EC_AffinePoint g_mul(const EC_Scalar &scalar, RandomNumberGenerator &rng)
Multiply by the group generator returning a complete point.
T serialize_uncompressed() const
static EC_AffinePoint generator(const EC_Group &group)
Return the standard group generator.
static EC_Group from_name(std::string_view name)
size_t get_order_bytes() const
static std::optional< EC_Scalar > deserialize(const EC_Group &group, std::span< const uint8_t > bytes)
static T serialize_pair(const EC_Scalar &r, const EC_Scalar &s)
static EC_Scalar from_bytes_mod_order(const EC_Group &group, std::span< const uint8_t > bytes)
static EC_Scalar random(const EC_Group &group, RandomNumberGenerator &rng)
static std::optional< std::pair< EC_Scalar, EC_Scalar > > deserialize_pair(const EC_Group &group, std::span< const uint8_t > bytes)
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
static std::unique_ptr< KDF > create_or_throw(std::string_view algo_spec, std::string_view provider="")
static std::unique_ptr< MessageAuthenticationCode > create_or_throw(std::string_view algo_spec, std::string_view provider="")
static std::unique_ptr< PasswordHashFamily > create_or_throw(std::string_view algo_spec, std::string_view provider="")
std::vector< uint8_t > process_message(std::span< const uint8_t > peer_message, RandomNumberGenerator &rng)
std::vector< uint8_t > generate_message(RandomNumberGenerator &rng)
secure_vector< uint8_t > shared_secret() const
ProverContext(const SystemParameters ¶ms, const ProverSecret &secret, std::span< const uint8_t > prover_id, std::span< const uint8_t > verifier_id, std::span< const uint8_t > context={})
static ProverSecret from_prehashed(EC_Scalar w0, EC_Scalar w1)
static ProverSecret from_password(const SystemParameters ¶ms, std::string_view password, std::span< const uint8_t > prover_id, std::span< const uint8_t > verifier_id, std::span< const uint8_t > salt)
static ProverSecret deserialize(const SystemParameters ¶ms, std::span< const uint8_t > secret)
RegistrationRecord registration_record(RandomNumberGenerator &rng) const
secure_vector< uint8_t > serialize() const
secure_vector< uint8_t > serialize() const
static RegistrationRecord deserialize(const SystemParameters ¶ms, std::span< const uint8_t > record)
static RegistrationRecord from_password(const SystemParameters ¶ms, std::string_view password, std::span< const uint8_t > prover_id, std::span< const uint8_t > verifier_id, std::span< const uint8_t > salt, RandomNumberGenerator &rng)
const EC_Group & group() const
const EC_AffinePoint & spake2p_n() const
static SystemParameters rfc9383_p256_sha512()
static SystemParameters rfc9383_p521_sha512()
static SystemParameters rfc9383_p384_sha256()
const std::string & hash_function() const
static SystemParameters rfc9383_p256_sha256()
static SystemParameters rfc9383_p384_sha512()
size_t confirmation_size() const
const EC_AffinePoint & spake2p_m() const
static SystemParameters custom(const EC_Group &group, std::span< const uint8_t > seed, std::string_view hash_fn)
size_t share_size() const
std::vector< uint8_t > process_message(std::span< const uint8_t > peer_message, RandomNumberGenerator &rng)
VerifierContext(const SystemParameters ¶ms, const RegistrationRecord &record, std::span< const uint8_t > prover_id, std::span< const uint8_t > verifier_id, std::span< const uint8_t > context={})
secure_vector< uint8_t > shared_secret() const
void verify_confirmation(std::span< const uint8_t > confirmation)
std::span< const uint8_t > as_span_of_bytes(const char *s, size_t len)
std::string fmt(std::string_view format, const T &... args)
constexpr auto store_le(ParamTs &&... params)
constexpr auto concat(Rs &&... ranges)
size_t hex_decode(uint8_t output[], const char input[], size_t input_length, size_t &input_consumed, bool ignore_ws)
const char * cast_uint8_ptr_to_char(const uint8_t *b)
std::vector< T, secure_allocator< T > > secure_vector
bool constant_time_compare(std::span< const uint8_t > x, std::span< const uint8_t > y)