library src.srp_util; import 'dart:math' as math; import 'dart:typed_data'; import 'package:pointycastle/pointycastle.dart'; class SRP6Util { static final _byteMask = BigInt.from(0xff); static BigInt calculateK(Digest digest, BigInt N, BigInt g) { return hashPaddedPair(digest, N, N, g); } static BigInt calculateU(Digest digest, BigInt N, BigInt? A, BigInt? B) { return hashPaddedPair(digest, N, A, B); } static BigInt calculateX(Digest digest, BigInt N, Uint8List salt, Uint8List identity, Uint8List password) { var output = Uint8List(digest.digestSize); digest.update(identity, 0, identity.length); digest.updateByte(':'.codeUnitAt(0)); digest.update(password, 0, password.length); digest.doFinal(output, 0); digest.update(salt, 0, salt.length); digest.update(output, 0, output.length); digest.doFinal(output, 0); return decodeBigInt(output); } /// Decode a BigInt from bytes in big-endian encoding. static BigInt decodeBigInt(List bytes) { var result = BigInt.from(0); for (var i = 0; i < bytes.length; i++) { result += BigInt.from(bytes[bytes.length - i - 1]) << (8 * i); } return result; } /// Encode a BigInt into bytes using big-endian encoding. static Uint8List encodeBigInt(BigInt number) { // Not handling negative numbers. Decide how you want to do that. var size = (number.bitLength + 7) >> 3; var result = Uint8List(size); for (var i = 0; i < size; i++) { result[size - i - 1] = (number & _byteMask).toInt(); number = number >> 8; } return result; } static BigInt? generatePrivateValue( Digest digest, BigInt N, BigInt g, SecureRandom random) { var minBits = math.min(256, N.bitLength ~/ 2); var min = BigInt.one << (minBits - 1); var max = N - BigInt.one; BigInt result; do { result = random.nextBigInteger(minBits); } while (result > max || result < min); return result; } static BigInt validatePublicValue(BigInt N, BigInt val) { val = val % N; // Check that val % N != 0 if (val == BigInt.zero) { throw Exception('Invalid public value: 0'); } return val; } /// Computes the client evidence message (M1) according to the standard routine: /// M1 = H( A | B | S ) /// [digest] The Digest used as the hashing function H /// [N] Modulus used to get the pad length /// [A] The public client value /// [B] The public server value /// [S] The secret calculated by both sides /// [M1] The calculated client evidence message static BigInt calculateM1( Digest digest, BigInt N, BigInt? A, BigInt? B, BigInt? S) { return hashPaddedTriplet(digest, N, A, B, S); } /// Computes the server evidence message (M2) according to the standard routine: /// M2 = H( A | M1 | S ) /// [digest] The Digest used as the hashing function H /// [N] Modulus used to get the pad length /// [A] The public client value /// [M1] The client evidence message /// [S] The secret calculated by both sides /// @return M2 The calculated server evidence message static BigInt calculateM2( Digest digest, BigInt N, BigInt? A, BigInt? M1, BigInt? S) { return hashPaddedTriplet(digest, N, A, M1, S); } /// Computes the final Key according to the standard routine: Key = H(S) /// [digest] The Digest used as the hashing function H /// [N] Modulus used to get the pad length /// [S] The secret calculated by both sides /// @return the final Key value. static BigInt calculateKey(Digest digest, BigInt N, BigInt? S) { var padLength = (N.bitLength + 7) ~/ 8; var S0 = getPadded(S!, padLength); digest.update(S0, 0, S0.length); var output = Uint8List(digest.digestSize); digest.doFinal(output, 0); return decodeBigInt(output); } static BigInt hashPaddedTriplet( Digest digest, BigInt N, BigInt? n1, BigInt? n2, BigInt? n3) { var padLength = (N.bitLength + 7) ~/ 8; var n1Bytes = getPadded(n1!, padLength); var n2Bytes = getPadded(n2!, padLength); var n3Bytes = getPadded(n3!, padLength); digest.update(n1Bytes, 0, n1Bytes.length); digest.update(n2Bytes, 0, n2Bytes.length); digest.update(n3Bytes, 0, n3Bytes.length); var output = Uint8List(digest.digestSize); digest.doFinal(output, 0); return decodeBigInt(output); } static Uint8List getPadded(BigInt n, int length) { var bs = encodeBigInt(n); if (bs.length < length) { var tmp = Uint8List(length); var start = length - bs.length; for (var i = 0; start < length; i++, start++) { tmp[start] = bs[i]; } bs = tmp; } return bs; } static BigInt hashPaddedPair( Digest digest, BigInt N, BigInt? n1, BigInt? n2) { var padLength = (N.bitLength + 7) ~/ 8; var n1Bytes = getPadded(n1!, padLength); var n2Bytes = getPadded(n2!, padLength); digest.update(n1Bytes, 0, n1Bytes.length); digest.update(n2Bytes, 0, n2Bytes.length); var output = Uint8List(digest.digestSize); digest.doFinal(output, 0); return decodeBigInt(output); } }