/// 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 publicKey) { final u = _littleEndian(publicKey) % _p; return _onCurve(u) && _ladderIsInfinity(u, _order); } BigInt _littleEndian(List 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; }