processMessage method
Processes the peer's SPAKE2 message and derives the shared key.
theirMessage is the 32-byte message received from the peer.
verbose enables detailed transcript logging.
Returns a 64-byte shared key.
Implementation
Uint8List processMessage(Uint8List theirMessage, {bool verbose = false}) {
if (!_messageGenerated) throw StateError('Must call generateMessage first');
if (_keyDerived) throw StateError('Key already derived');
_keyDerived = true;
// Decode their point
final theirPoint = Ed25519.decodePoint(theirMessage);
// Choose the opposite blind point (peer used the other one)
final theirBlindPoint = _role == Spake2Role.alice ? Ed25519.pointN : Ed25519.pointM;
// Unblind: remove w * theirBlindPoint from their message
// unblinded = theirPoint - w * theirBlindPoint
final wTheirBlind = Ed25519.scalarMult(_passwordScalar, theirBlindPoint);
final unblinded = Ed25519.pointSub(theirPoint, wTheirBlind);
// Compute shared secret: K = private_key * unblinded
final sharedPoint = Ed25519.scalarMult(_privateKey, unblinded);
_sharedEncoded = Ed25519.encodePoint(sharedPoint);
final sharedEncoded = _sharedEncoded!;
// Build the transcript for key derivation
// transcript = len(myName) || myName || len(theirName) || theirName ||
// len(myMessage) || myMessage || len(theirMessage) || theirMessage ||
// len(sharedEncoded) || sharedEncoded || len(passwordScalar) || passwordScalar
final transcript = BytesBuilder();
// Order depends on role: Alice's message first, then Bob's
final Uint8List firstMsg;
final Uint8List secondMsg;
final Uint8List firstName;
final Uint8List secondName;
if (_role == Spake2Role.alice) {
firstMsg = _myMessage;
secondMsg = theirMessage;
firstName = _myName;
secondName = _theirName;
} else {
firstMsg = theirMessage;
secondMsg = _myMessage;
firstName = _theirName;
secondName = _myName;
}
void append(String label, Uint8List data) {
if (verbose) print('Transcript $label (${data.length} bytes): ${_toHex(data)}');
_appendLengthPrefixed(transcript, data);
}
if (verbose) print('--- SPAKE2 Transcript Debug ---');
append('A', firstName);
append('B', secondName);
append('X', firstMsg);
append('Y', secondMsg);
append('K', sharedEncoded);
append('w', _passwordHash);
if (verbose) print('--- End SPAKE2 Transcript Debug ---');
final hash = SHA512Digest();
final transcriptBytes = transcript.toBytes();
final key = Uint8List(64);
hash.update(transcriptBytes, 0, transcriptBytes.length);
hash.doFinal(key, 0);
return key;
}