| // 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 |