blob: 2ca85ce484a87cdc2ce3284cb16971367fac5f95 [file]
// Copyright 2024 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OPENSSL_HEADER_CRYPTO_SPAKE2PLUS_INTERNAL_H
#define OPENSSL_HEADER_CRYPTO_SPAKE2PLUS_INTERNAL_H
#include <openssl/base.h>
#include <sys/types.h>
#include <openssl/sha2.h>
#include <openssl/span.h>
#include "../fipsmodule/ec/internal.h"
BSSL_NAMESPACE_BEGIN
// SPAKE2+.
//
// SPAKE2+ is an augmented password-authenticated key-exchange. It allows
// two parties, a prover and verifier, to derive a strong shared key with no
// risk of disclosing the password, known only to the prover, to the verifier.
// (But note that the verifier can still attempt an offline, brute-force attack
// to recover the password.)
//
// This is an implementation of SPAKE2+ using P-256 as the group, SHA-256 as
// the hash function, HKDF-SHA256 as the key derivation function, and
// HMAC-SHA256 as the message authentication code.
//
// See https://www.rfc-editor.org/rfc/rfc9383.html
namespace spake2plus {
// kShareSize is the size of a SPAKE2+ key share.
constexpr size_t kShareSize = 65;
// kConfirmSize is the size of a SPAKE2+ key confirmation message.
constexpr size_t kConfirmSize = 32;
// kVerifierSize is the size of the w0 and w1 values in the SPAKE2+ protocol.
constexpr size_t kVerifierSize = 32;
// kRegistrationRecordSize is the number of bytes in a registration record,
// which is provided to the verifier.
constexpr size_t kRegistrationRecordSize = 65;
// kSecretSize is the number of bytes of shared secret that the SPAKE2+ protocol
// generates.
constexpr size_t kSecretSize = 32;
// Register computes the values needed in the offline registration
// step of the SPAKE2+ protocol. See the following for more details:
// https://www.rfc-editor.org/rfc/rfc9383.html#section-3.2
//
// The `password` argument is the mandatory prover password. The `out_w0`,
// `out_w1`, and `out_registration_record` arguments are where the password
// verifiers (w0 and w1) and registration record (L) are stored, respectively.
// The prover is given `out_w0` and `out_w1` while the verifier is given
// `out_w0` and `out_registration_record`.
//
// To ensure success, `out_w0` and `out_w1` must be of length `kVerifierSize`,
// and `out_registration_record` of size `kRegistrationRecordSize`.
[[nodiscard]] OPENSSL_EXPORT bool Register(
Span<uint8_t> out_w0, Span<uint8_t> out_w1,
Span<uint8_t> out_registration_record, Span<const uint8_t> password,
Span<const uint8_t> id_prover, Span<const uint8_t> id_verifier);
class OPENSSL_EXPORT Prover {
public:
static constexpr bool kAllowUniquePtr = true;
Prover();
~Prover();
// Init creates a new prover, which can only be used for a single execution of
// the protocol.
//
// The `context` argument is an application-specific value meant to constrain
// the protocol execution. The `w0` and `w1` arguments are password verifier
// values computed during the offline registration phase of the protocol. The
// `id_prover` and `id_verifier` arguments allow optional, opaque names to be
// bound into the protocol. See the following for more information about how
// these identities may be chosen:
// https://www.rfc-editor.org/rfc/rfc9383.html#name-definition-of-spake2
[[nodiscard]] bool Init(Span<const uint8_t> context,
Span<const uint8_t> id_prover,
Span<const uint8_t> id_verifier,
Span<const uint8_t> w0, Span<const uint8_t> w1,
Span<const uint8_t> x = Span<const uint8_t>());
// GenerateShare computes a SPAKE2+ share and writes it to `out_share`.
//
// This function can only be called once for a given `Prover`. To ensure
// success, `out_share` must be `kShareSize` bytes.
[[nodiscard]] bool GenerateShare(Span<uint8_t> out_share);
// ComputeConfirmation computes a SPAKE2+ key confirmation
// message and writes it to `out_confirm`. It also computes the shared secret
// and writes it to `out_secret`.
//
// This function can only be called once for a given `Prover`.
//
// To ensure success, `out_confirm` must be `kConfirmSize` bytes
// and `out_secret` must be `kSecretSize` bytes.
[[nodiscard]] bool ComputeConfirmation(Span<uint8_t> out_confirm,
Span<uint8_t> out_secret,
Span<const uint8_t> peer_share,
Span<const uint8_t> peer_confirm);
private:
enum class State {
kInit,
kShareGenerated,
kConfirmGenerated,
kDone,
};
State state_ = State::kInit;
SHA256_CTX transcript_hash_;
EC_SCALAR w0_;
EC_SCALAR w1_;
EC_SCALAR x_;
EC_AFFINE X_;
uint8_t share_[kShareSize];
};
class OPENSSL_EXPORT Verifier {
public:
static constexpr bool kAllowUniquePtr = true;
Verifier();
~Verifier();
// Init creates a new verifier, which can only be used for a single execution
// of the protocol.
//
// The `context` argument is an application-specific value meant to constrain
// the protocol execution. The `w0` and `registration_record` arguments are
// required, and are computed by the prover via `Register`. Only the prover
// can produce `w0` and `registration_record`, as they require
// knowledge of the password. The prover must securely transmit this to the
// verifier out-of-band. The `id_prover` and `id_verifier` arguments allow
// optional, opaque names to be bound into the protocol. See the following for
// more information about how these identities may be chosen:
// https://www.rfc-editor.org/rfc/rfc9383.html#name-definition-of-spake2
[[nodiscard]] bool Init(Span<const uint8_t> context,
Span<const uint8_t> id_prover,
Span<const uint8_t> id_verifier,
Span<const uint8_t> w0,
Span<const uint8_t> registration_record,
Span<const uint8_t> y = Span<const uint8_t>());
// ProcessProverShare computes a SPAKE2+ share from an input share,
// `prover_share`, and writes it to `out_share`. It also computes the key
// confirmation message and writes it to `out_confirm`. Finally, it computes
// the shared secret and writes it to `out_secret`.
//
// This function can only be called once for a given `Verifier`.
//
// To ensure success, `out_share` must be `kShareSize` bytes, `out_confirm`
// must be `kConfirmSize` bytes, and `out_secret` must be `kSecretSize` bytes.
[[nodiscard]] bool ProcessProverShare(Span<uint8_t> out_share,
Span<uint8_t> out_confirm,
Span<uint8_t> out_secret,
Span<const uint8_t> prover_share);
// VerifyProverConfirmation verifies a SPAKE2+ key confirmation message,
// `prover_confirm`.
//
// This function can only be called once for a given `Verifier`.
[[nodiscard]] bool VerifyProverConfirmation(Span<const uint8_t> peer_confirm);
private:
enum class State {
kInit,
kProverShareSeen,
kDone,
};
State state_ = State::kInit;
SHA256_CTX transcript_hash_;
EC_SCALAR w0_;
EC_AFFINE L_;
EC_SCALAR y_;
uint8_t confirm_[kConfirmSize];
};
} // namespace spake2plus
BSSL_NAMESPACE_END
#endif // OPENSSL_HEADER_CRYPTO_SPAKE2PLUS_INTERNAL_H