fix(auth): decoy encryption keys are keys a device could have made
An address nobody has got, as the recovery phrase's first step, an X25519 public key of 32 HMAC bytes, which is a point of Curve25519's prime-order subgroup one time in sixteen, as every real key is: whoever checked could tell nearly always whether an address has an account (the second reading's M1). The decoy's key is now made from a seed, as its Ed25519 key already was. The tests hold twenty decoys to the curve check the review used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
aea0c447fc
commit
009970faab
1 changed files with 137 additions and 8 deletions
|
|
@ -0,0 +1,73 @@
|
|||
/// Whether 32 bytes are an X25519 public key a device could have made: a
|
||||
/// point of Curve25519 (not of its twist) in its prime-order subgroup, as
|
||||
/// every key made from a scalar is. Random bytes are such a point one time
|
||||
/// in sixteen, so an answer for an address nobody has that isn't one would
|
||||
/// tell it from an account's (the second reading's M1).
|
||||
library;
|
||||
|
||||
final _p = (BigInt.one << 255) - BigInt.from(19);
|
||||
final _a24 = BigInt.from(121665);
|
||||
|
||||
/// The prime order of the subgroup the base point generates.
|
||||
final _order =
|
||||
(BigInt.one << 252) +
|
||||
BigInt.parse('27742317777372353535851937790883648493');
|
||||
|
||||
/// Whether [publicKey] (X25519's u-coordinate, little-endian) is a point
|
||||
/// of the prime-order subgroup.
|
||||
bool isX25519PublicKey(List<int> publicKey) {
|
||||
final u = _littleEndian(publicKey) % _p;
|
||||
return _onCurve(u) && _ladderIsInfinity(u, _order);
|
||||
}
|
||||
|
||||
BigInt _littleEndian(List<int> bytes) {
|
||||
var value = BigInt.zero;
|
||||
for (var i = bytes.length - 1; i >= 0; i--) {
|
||||
value = (value << 8) | BigInt.from(bytes[i]);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Whether u³ + Au² + u is a square: the point is on the curve, not its
|
||||
/// twist.
|
||||
bool _onCurve(BigInt u) {
|
||||
final rhs = (u * u * u + BigInt.from(486662) * u * u + u) % _p;
|
||||
return rhs == BigInt.zero ||
|
||||
rhs.modPow((_p - BigInt.one) >> 1, _p) == BigInt.one;
|
||||
}
|
||||
|
||||
/// Whether [k] times the point at [u] is the point at infinity (an x-only
|
||||
/// Montgomery ladder, RFC 7748's).
|
||||
bool _ladderIsInfinity(BigInt u, BigInt k) {
|
||||
var x2 = BigInt.one;
|
||||
var z2 = BigInt.zero;
|
||||
var x3 = u;
|
||||
var z3 = BigInt.one;
|
||||
var swap = 0;
|
||||
for (var t = k.bitLength - 1; t >= 0; t--) {
|
||||
final bit = ((k >> t) & BigInt.one).toInt();
|
||||
swap ^= bit;
|
||||
if (swap == 1) {
|
||||
(x2, x3) = (x3, x2);
|
||||
(z2, z3) = (z3, z2);
|
||||
}
|
||||
swap = bit;
|
||||
final a = (x2 + z2) % _p;
|
||||
final aa = a * a % _p;
|
||||
final b = (x2 - z2) % _p;
|
||||
final bb = b * b % _p;
|
||||
final e = (aa - bb) % _p;
|
||||
final c = (x3 + z3) % _p;
|
||||
final d = (x3 - z3) % _p;
|
||||
final da = d * a % _p;
|
||||
final cb = c * b % _p;
|
||||
x3 = (da + cb) * (da + cb) % _p;
|
||||
z3 = u * ((da - cb) * (da - cb) % _p) % _p;
|
||||
x2 = aa * bb % _p;
|
||||
z2 = e * ((aa + _a24 * e) % _p) % _p;
|
||||
}
|
||||
if (swap == 1) {
|
||||
z2 = z3;
|
||||
}
|
||||
return z2 % _p == BigInt.zero;
|
||||
}
|
||||
Loading…
Reference in a new issue