library impl.srp_server;

import 'package:pointycastle/api.dart';
import 'package:pointycastle/srp/srp6_standard_groups.dart';
import 'package:pointycastle/srp/srp6_util.dart';

class SRP6Server implements SRPServer {
  late BigInt N;
  late BigInt g;

  BigInt v;
  SecureRandom random;
  Digest digest;

  BigInt? A;

  BigInt? b;
  BigInt? B;

  BigInt? u;
  BigInt? S;
  BigInt? M1;
  BigInt? M2;
  BigInt? Key;

  SRP6Server(
      {required SRP6GroupParameters group,
      required this.v,
      required this.digest,
      required this.random}) {
    g = group.g;
    N = group.N;
  }

  @override
  BigInt? calculateSecret(BigInt clientA) {
    A = SRP6Util.validatePublicValue(N, clientA);
    u = SRP6Util.calculateU(digest, N, A, B);
    S = _calculateS();

    return S;
  }

  @override
  BigInt? calculateServerEvidenceMessage() {
    // Verify pre-requirements
    if (A == null || M1 == null || S == null) {
      throw Exception(
          'Impossible to compute M2: some data are missing from the previous operations (A,M1,S)');
    }

    // Compute the server evidence message 'M2'
    M2 = SRP6Util.calculateM2(digest, N, A!, M1!, S!);
    return M2;
  }

  @override
  BigInt? calculateSessionKey() {
    // Verify pre-requirements
    if (S == null || M1 == null || M2 == null) {
      throw Exception(
          'Impossible to compute Key: some data are missing from the previous operations (S,M1,M2)');
    }
    Key = SRP6Util.calculateKey(digest, N, S);
    return Key;
  }

  @override
  BigInt? generateServerCredentials() {
    var k = SRP6Util.calculateK(digest, N, g);
    b = selectPrivateValue();
    B = (k * v + g.modPow(b!, N)) % N;

    return B;
  }

  BigInt? selectPrivateValue() {
    return SRP6Util.generatePrivateValue(digest, N, g, random);
  }

  @override
  bool verifyClientEvidenceMessage(BigInt clientM1) {
    // Verify pre-requirements
    if (A == null || B == null || S == null) {
      throw Exception(
          'Impossible to compute and verify M1: some data are missing from the previous operations (A,B,S)');
    }

    // Compute the own client evidence message 'M1'
    var computedM1 = SRP6Util.calculateM1(digest, N, A, B, S);
    if (computedM1.compareTo(clientM1) == 0) {
      M1 = clientM1;
      return true;
    }
    return false;
  }

  BigInt _calculateS() {
    return ((v.modPow(u!, N) * A!) % N).modPow(b!, N);
  }
}
