// 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 #include #include #include #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 out_w0, Span out_w1, Span out_registration_record, Span password, Span id_prover, Span 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 context, Span id_prover, Span id_verifier, Span w0, Span w1, Span x = Span()); // 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 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 out_confirm, Span out_secret, Span peer_share, Span 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 context, Span id_prover, Span id_verifier, Span w0, Span registration_record, Span y = Span()); // 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 out_share, Span out_confirm, Span out_secret, Span 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 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