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