1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
// 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 <contrib/restricted/google/boringssl/include/openssl/base.h>
#include <sys/types.h>
#include <contrib/restricted/google/boringssl/include/openssl/sha2.h>
#include <contrib/restricted/google/boringssl/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
|