generateMessage method

Uint8List generateMessage(
  1. Uint8List password
)

Generates the SPAKE2 message to send to the peer.

password is the raw pairing code bytes (UTF-8 encoded). Returns a 32-byte compressed Ed25519 point.

Implementation

Uint8List generateMessage(Uint8List password) {
  if (_messageGenerated) throw StateError('Message already generated');
  _messageGenerated = true;

  // Generate random private scalar
  final randomScalarBytes = _generatePrivateScalar();
  final privateScalar = Ed25519.leBytesToBigInt(randomScalarBytes);
  // Multiply by cofactor (8)
  final cofactorPrivateScalar = (privateScalar << 3) % (BigInt.one << 256);
  _privateKey = Ed25519.bigIntToLeBytes(cofactorPrivateScalar, 32);

  // Compute password scalar (w) by hashing password and reducing mod L
  // BoringSSL uses the raw hash for the transcript but the reduced scalar for points.
  _passwordHash = _passwordToHash(password);
  var w = Ed25519.leBytesToBigInt(_passwordHash) % Ed25519.L;

  // The BoringSSL "password scalar hack": ensure bottom 3 bits are zero.
  // We add multiples of L (the group order) until the bottom 3 bits are zero.
  // L is odd, so adding L flips the LSB.
  if (w.isOdd) w += Ed25519.L;
  if ((w >> 1).isOdd) w += (Ed25519.L << 1);
  if ((w >> 2).isOdd) w += (Ed25519.L << 2);
  _passwordScalar = Ed25519.bigIntToLeBytes(w, 32);

  // Compute T = private_key * B (base point multiplication)
  final tPoint = Ed25519.scalarMultBase(_privateKey);

  // Choose M or N based on role
  final blindPoint = _role == Spake2Role.alice ? Ed25519.pointM : Ed25519.pointN;

  // Compute message = T + w * blind_point
  final wBlind = Ed25519.scalarMult(_passwordScalar, blindPoint);
  final messagePoint = Ed25519.pointAdd(tPoint, wBlind);

  _myMessage = Ed25519.encodePoint(messagePoint);
  return Uint8List.fromList(_myMessage);
}