-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrfc_vectors_test.cpp
More file actions
276 lines (246 loc) · 13.2 KB
/
rfc_vectors_test.cpp
File metadata and controls
276 lines (246 loc) · 13.2 KB
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// RFC 9382 Test Vectors Validation - Clean Implementation
// Tests the /lib_rfc SPAKE2 implementation against official RFC test vectors
#include "lib_rfc/spake2.h"
#include "lib_rfc/spake2_constants.h"
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
#include <sstream>
// Helper function to convert hex string to bytes
std::vector<uint8_t> hexToBytes(const std::string& hex) {
std::vector<uint8_t> bytes;
std::string cleanHex = hex;
if (cleanHex.substr(0, 2) == "0x") {
cleanHex = cleanHex.substr(2);
}
for (size_t i = 0; i < cleanHex.length(); i += 2) {
std::string byteString = cleanHex.substr(i, 2);
uint8_t byte = static_cast<uint8_t>(std::strtol(byteString.c_str(), nullptr, 16));
bytes.push_back(byte);
}
return bytes;
}
// Helper function to convert bytes to hex string
std::string bytesToHex(const std::vector<uint8_t>& bytes) {
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (uint8_t byte : bytes) {
ss << std::setw(2) << static_cast<int>(byte);
}
return ss.str();
}
// RFC 9382 Test Vector structure
struct RFCTestVector {
std::string name;
std::string idA; // Identity A
std::string idB; // Identity B
std::string mHex; // M point (compressed hex)
std::string nHex; // N point (compressed hex)
std::string wHex; // Password scalar w (hex)
std::string xHex; // Random scalar x for A (hex)
std::string yHex; // Random scalar y for B (hex)
std::string pAHex; // Expected pA = w*M + X (uncompressed hex)
std::string pBHex; // Expected pB = w*N + Y (uncompressed hex)
std::string kHex; // Expected shared point K (uncompressed hex)
std::string keHex; // Expected encryption key Ke (hex)
std::string kaHex; // Expected authentication key Ka (hex)
std::string kcAHex; // Expected confirmation key for A (hex)
std::string kcBHex; // Expected confirmation key for B (hex)
std::string aConfHex; // Expected A's confirmation message (hex)
std::string bConfHex; // Expected B's confirmation message (hex)
};
// Test complete protocol flow against RFC vectors
bool testRFCVector(const RFCTestVector& vector) {
std::cout << "\n=== Testing: " << vector.name << " ===\n";
std::cout << "Identity A: " << (vector.idA.empty() ? "(empty)" : vector.idA) << "\n";
std::cout << "Identity B: " << (vector.idB.empty() ? "(empty)" : vector.idB) << "\n";
try {
// Create options with debug mode enabled for deterministic testing
auto clientOptions = spake2::DefaultOptions();
clientOptions->debug = true;
clientOptions->skipPasswordDerivation = true;
clientOptions->identityA = std::vector<uint8_t>(vector.idA.begin(), vector.idA.end());
clientOptions->identityB = std::vector<uint8_t>(vector.idB.begin(), vector.idB.end());
clientOptions->debugX = hexToBytes(vector.xHex);
clientOptions->debugY = hexToBytes(vector.yHex);
auto serverOptions = spake2::DefaultOptions();
serverOptions->debug = true;
serverOptions->skipPasswordDerivation = true;
serverOptions->identityA = std::vector<uint8_t>(vector.idA.begin(), vector.idA.end());
serverOptions->identityB = std::vector<uint8_t>(vector.idB.begin(), vector.idB.end());
serverOptions->debugX = hexToBytes(vector.xHex);
serverOptions->debugY = hexToBytes(vector.yHex);
auto password = hexToBytes(vector.wHex);
// Create client and server instances
auto client = spake2::SPAKE2::createClient(password, clientOptions);
auto server = spake2::SPAKE2::createServer(password, serverOptions);
// Step 1: Client starts the protocol
auto clientMsg = client->start();
// Verify client message matches RFC vector
auto expectedPA = hexToBytes(vector.pAHex);
if (clientMsg != expectedPA) {
std::cout << "✗ Client message (pA) mismatch\n";
return false;
}
std::cout << "✓ Client message (pA) matches RFC vector\n";
// Step 2: Server processes client's message and responds
auto serverMsg = server->exchange(clientMsg);
// Verify server message matches RFC vector
auto expectedPB = hexToBytes(vector.pBHex);
if (serverMsg != expectedPB) {
std::cout << "✗ Server message (pB) mismatch\n";
return false;
}
std::cout << "✓ Server message (pB) matches RFC vector\n";
// Step 3: Client finishes and generates confirmation
auto clientConfirm = client->finish(serverMsg);
// Verify client confirmation matches RFC vector
auto expectedAConf = hexToBytes(vector.aConfHex);
if (clientConfirm != expectedAConf) {
std::cout << "✗ Client confirmation (cA) mismatch\n";
return false;
}
std::cout << "✓ Client confirmation (cA) matches RFC vector\n";
// Step 4: Server validates client's confirmation and generates its own
auto serverConfirm = server->confirm(clientConfirm);
// Verify server confirmation matches RFC vector
auto expectedBConf = hexToBytes(vector.bConfHex);
if (serverConfirm != expectedBConf) {
std::cout << "✗ Server confirmation (cB) mismatch\n";
return false;
}
std::cout << "✓ Server confirmation (cB) matches RFC vector\n";
// Step 5: Client verifies server's confirmation
client->verify(serverConfirm);
std::cout << "✓ Client verification successful\n";
// Step 6: Verify shared keys match
auto clientKey = client->getSharedKey();
auto serverKey = server->getSharedKey();
if (clientKey != serverKey) {
std::cout << "✗ Shared keys do not match\n";
return false;
}
std::cout << "✓ Shared keys match\n";
// Verify shared key matches RFC vector
auto expectedKe = hexToBytes(vector.keHex);
if (clientKey.size() >= expectedKe.size()) {
std::vector<uint8_t> derivedKe(clientKey.begin(), clientKey.begin() + expectedKe.size());
if (derivedKe != expectedKe) {
std::cout << "✗ Derived encryption key (Ke) mismatch\n";
return false;
}
std::cout << "✓ Derived encryption key (Ke) matches RFC vector\n";
}
std::cout << "✓ PASSED: Complete protocol flow for " << vector.name << "\n";
return true;
} catch (const std::exception& e) {
std::cout << "✗ FAILED: " << vector.name << " - Exception: " << e.what() << "\n";
return false;
}
}
int main() {
std::cout << "RFC 9382 Test Vectors Validation\n";
std::cout << "Using /lib_rfc SPAKE2 Implementation\n";
std::cout << "====================================\n";
// Official RFC 9382 Test Vectors
std::vector<RFCTestVector> testVectors = {
{
.name = "P-256 Test Vector 1 from RFC 9382",
.idA = "server",
.idB = "client",
.mHex = spake2::P256_M_HEX,
.nHex = spake2::P256_N_HEX,
.wHex = "0x2ee57912099d31560b3a44b1184b9b4866e904c49d12ac5042c97dca461b1a5f",
.xHex = "0x43dd0fd7215bdcb482879fca3220c6a968e66d70b1356cac18bb26c84a78d729",
.yHex = "0xdcb60106f276b02606d8ef0a328c02e4b629f84f89786af5befb0bc75b6e66be",
.pAHex = "0x04a56fa807caaa53a4d28dbb9853b9815c61a411118a6fe516a8798434751470f9010153ac33d0d5f2047ffdb1a3e42c9b4e6be662766e1eeb4116988ede5f912c",
.pBHex = "0x0406557e482bd03097ad0cbaa5df82115460d951e3451962f1eaf4367a420676d09857ccbc522686c83d1852abfa8ed6e4a1155cf8f1543ceca528afb591a1e0b7",
.kHex = "0x0412af7e89717850671913e6b469ace67bd90a4df8ce45c2af19010175e37eed69f75897996d539356e2fa6a406d528501f907e04d97515fbe83db277b715d3325",
.keHex = "0x0e0672dc86f8e45565d338b0540abe69",
.kaHex = "0x15bdf72e2b35b5c9e5663168e960a91b",
.kcAHex = "0x00c12546835755c86d8c0db7851ae86f",
.kcBHex = "0xa9fa3406c3b781b93d804485430ca27a",
.aConfHex = "0x58ad4aa88e0b60d5061eb6b5dd93e80d9c4f00d127c65b3b35b1b5281fee38f0",
.bConfHex = "0xd3e2e547f1ae04f2dbdbf0fc4b79f8ecff2dff314b5d32fe9fcef2fb26dc459b"
},
{
.name = "P-256 Test Vector 2 from RFC 9382",
.idA = "",
.idB = "client",
.mHex = spake2::P256_M_HEX,
.nHex = spake2::P256_N_HEX,
.wHex = "0x0548d8729f730589e579b0475a582c1608138ddf7054b73b5381c7e883e2efae",
.xHex = "0x403abbe3b1b4b9ba17e3032849759d723939a27a27b9d921c500edde18ed654b",
.yHex = "0x903023b6598908936ea7c929bd761af6039577a9c3f9581064187c3049d87065",
.pAHex = "0x04a897b769e681c62ac1c2357319a3d363f610839c4477720d24cbe32f5fd85f44fb92ba966578c1b712be6962498834078262caa5b441ecfa9d4a9485720e918a",
.pBHex = "0x04e0f816fd1c35e22065d5556215c097e799390d16661c386e0ecc84593974a61b881a8c82327687d0501862970c64565560cb5671f696048050ca66ca5f8cc7fc",
.kHex = "0x048f83ec9f6e4f87cc6f9dc740bdc2769725f923364f01c84148c049a39a735ebda82eac03e00112fd6a5710682767cff5361f7e819e53d8d3c3a2922e0d837aa6",
.keHex = "0x642f05c473c2cd79909f9a841e2f30a7",
.kaHex = "0x0bf89b18180af97353ba198789c2b963",
.kcAHex = "0xc6be376fc7cd1301fd0a13adf3e7bffd",
.kcBHex = "0xb7243f4ae60440a49b3f8cab3c1fba07",
.aConfHex = "0x47d29e6666af1b7dd450d571233085d7a9866e4d49d2645e2df975489521232b",
.bConfHex = "0x3313c5cefc361d27fb16847a91c2a73b766ffa90a4839122a9b70a2f6bd1d6df"
},
{
.name = "P-256 Test Vector 3 from RFC 9382",
.idA = "server",
.idB = "",
.mHex = spake2::P256_M_HEX,
.nHex = spake2::P256_N_HEX,
.wHex = "0x626e0cdc7b14c9db3e52a0b1b3a768c98e37852d5db30febe0497b14eae8c254",
.xHex = "0x07adb3db6bc623d3399726bfdbfd3d15a58ea776ab8a308b00392621291f9633",
.yHex = "0xb6a4fc8dbb629d4ba51d6f91ed1532cf87adec98f25dd153a75accafafedec16",
.pAHex = "0x04f88fb71c99bfffaea370966b7eb99cd4be0ff1a7d335caac4211c4afd855e2e15a873b298503ad8ba1d9cbb9a392d2ba309b48bfd7879aefd0f2cea6009763b0",
.pBHex = "0x040c269d6be017dccb15182ac6bfcd9e2a14de019dd587eaf4bdfd353f031101e7cca177f8eb362a6e83e7d5e729c0732e1b528879c086f39ba0f31a9661bd34db",
.kHex = "0x0445ee233b8ecb51ebd6e7da3f307e88a1616bae2166121221fdc0dadb986afaf3ec8a988dc9c626fa3b99f58a7ca7c9b844bb3e8dd9554aafc5b53813504c1cbe",
.keHex = "0x005184ff460da2ce59062c87733c299c",
.kaHex = "0x3521297d736598fc0a1127600efa1afb",
.kcAHex = "0xf3da53604f0aeecea5a33be7bddf6edf",
.kcBHex = "0x9e3f86848736f159bd92b6e107ec6799",
.aConfHex = "0xbc9f9bbe99f26d0b2260e6456e05a86196a3307ec6663a18bf6ac825736533b2",
.bConfHex = "0xc2370e1bf813b086dff0d834e74425a06e6390f48f5411900276dcccc5a297ec"
},
{
.name = "P-256 Test Vector 4 from RFC 9382",
.idA = "",
.idB = "",
.mHex = spake2::P256_M_HEX,
.nHex = spake2::P256_N_HEX,
.wHex = "0x7bf46c454b4c1b25799527d896508afd5fc62ef4ec59db1efb49113063d70cca",
.xHex = "0x8cef65df64bb2d0f83540c53632de911b5b24b3eab6cc74a97609fd659e95473",
.yHex = "0xd7a66f64074a84652d8d623a92e20c9675c61cb5b4f6a0063e4648a2fdc02d53",
.pAHex = "0x04a65b367a3f613cf9f0654b1b28a1e3a8a40387956c8ba6063e8658563890f46ca1ef6a676598889fc28de2950ab8120b79a5ef1ea4c9f44bc98f585634b46d66",
.pBHex = "0x04589f13218822710d98d8b2123a079041052d9941b9cf88c6617ddb2fcc0494662eea8ba6b64692dc318250030c6af045cb738bc81ba35b043c3dcb46adf6f58d",
.kHex = "0x041a3c03d51b452537ca2a1fea6110353c6d5ed483c4f0f86f4492ca3f378d40a994b4477f93c64d928edbbcd3e85a7c709b7ea73ee97986ce3d1438e135543772",
.keHex = "0xfc6374762ba5cf11f4b2caa08b2cd1b9",
.kaHex = "0x907ae0e26e8d6234318d91583cd74c86",
.kcAHex = "0x5dbd2f477166b7fb6d61febbd77a5563",
.kcBHex = "0x7689b4654407a5faeffdc8f18359d8a3",
.aConfHex = "0xdfb4db8d48ae5a675963ea5e6c19d98d4ea028d8e898dad96ea19a80ade95dca",
.bConfHex = "0xd0f0609d1613138d354f7e95f19fb556bf52d751947241e8c7118df5ef0ae175"
}
};
int passed = 0;
int total = testVectors.size();
// Test each RFC vector
for (const auto& vector : testVectors) {
if (testRFCVector(vector)) {
passed++;
}
}
// Summary
std::cout << "\n=== Test Summary ===\n";
std::cout << "Passed: " << passed << "/" << total << "\n";
std::cout << "Success Rate: " << (100.0 * passed / total) << "%\n";
if (passed == total) {
std::cout << "\n✓ All RFC 9382 test vectors passed!\n";
std::cout << "✓ Debug interface working correctly\n";
std::cout << "✓ Protocol messages match RFC specification\n";
return 0;
} else {
std::cout << "\n✗ Some test vectors failed\n";
return 1;
}
}