// See file LICENSE for more information.

library impl.digest.sm3;

import 'dart:typed_data';

import 'package:pointycastle/api.dart';
import 'package:pointycastle/src/impl/md4_family_digest.dart';
import 'package:pointycastle/src/registry/registry.dart';
import 'package:pointycastle/src/ufixnum.dart';

/// Implementation of Chinese SM3 Hash Function (GM/T 0004-2012) as
/// described at [Reference 1][REF1] and at [Reference 2][REF2].
///
/// 这是一个依据 [参考文献1][REF1] 和 [参考文献2][REF2] 的 SM3 哈希函数的实现.
///
/// [REF1]: http://www.sca.gov.cn/sca/xwdt/2010-12/17/content_1002389.shtml
/// [REF2]: https://tools.ietf.org/html/draft-shen-sm3-hash
class SM3Digest extends MD4FamilyDigest implements Digest {
  static final FactoryConfig factoryConfig =
      StaticFactoryConfig(Digest, 'SM3', () => SM3Digest());

  static const _DIGEST_LENGTH = 32;

  final List<int> _W;

  SM3Digest()
      : _W = List<int>.filled(68, 0, growable: false),
        super(Endian.big, 8, 16);

  @override
  final algorithmName = 'SM3';
  @override
  final digestSize = _DIGEST_LENGTH;

  @override
  void resetState() {
    state[0] = 0x7380166f;
    state[1] = 0x4914b2b9;
    state[2] = 0x172442d7;
    state[3] = 0xda8a0600;
    state[4] = 0xa96f30bc;
    state[5] = 0x163138aa;
    state[6] = 0xe38dee4d;
    state[7] = 0xb0fb0e4e;
  }

  @override
  void processBlock() {
    int i;
    // [REF1] 5.3.2 消息扩展
    // [REF2] 3.3.2 Message Extension
    _W.setAll(0, buffer);
    for (i = 16; i < 68; ++i) {
      _W[i] = _P1(_W[i - 16] ^ _W[i - 9] ^ rotl32(_W[i - 3], 15)) ^
          rotl32(_W[i - 13], 7) ^
          _W[i - 6];
    }
    // [REF1] 5.3.3 压缩函数
    // [REF2] 3.3.3 Compression Function
    var A = state[0];
    var B = state[1];
    var C = state[2];
    var D = state[3];
    var E = state[4];
    var F = state[5];
    var G = state[6];
    var H = state[7];
    int SS1, SS2, TT1, TT2, Tj;
    for (i = 0; i < 16; ++i) {
      Tj = 0x79cc4519;
      SS1 = rotl32(clip32(rotl32(A, 12) + E + rotl32(Tj, i)), 7);
      SS2 = SS1 ^ rotl32(A, 12);
      TT1 = clip32(_FF1(A, B, C) + D + SS2 + (_W[i] ^ _W[i + 4]));
      TT2 = clip32(_GG1(E, F, G) + H + SS1 + _W[i]);
      D = C;
      C = rotl32(B, 9);
      B = A;
      A = TT1;
      H = G;
      G = rotl32(F, 19);
      F = E;
      E = _P0(TT2);
    }
    for (i = 16; i < 64; ++i) {
      Tj = 0x7a879d8a;
      SS1 = rotl32(clip32(rotl32(A, 12) + E + rotl32(Tj, i)), 7);
      SS2 = SS1 ^ rotl32(A, 12);
      TT1 = clip32(_FF2(A, B, C) + D + SS2 + (_W[i] ^ _W[i + 4]));
      TT2 = clip32(_GG2(E, F, G) + H + SS1 + _W[i]);
      D = C;
      C = rotl32(B, 9);
      B = A;
      A = TT1;
      H = G;
      G = rotl32(F, 19);
      F = E;
      E = _P0(TT2);
    }
    state[0] ^= A;
    state[1] ^= B;
    state[2] ^= C;
    state[3] ^= D;
    state[4] ^= E;
    state[5] ^= F;
    state[6] ^= G;
    state[7] ^= H;
  }

  /// FF1 Function
  static int _FF1(int X, int Y, int Z) => X ^ Y ^ Z;

  /// FF2 Function
  static int _FF2(int X, int Y, int Z) => (X & Y) | (X & Z) | (Y & Z);

  /// GG1 Function
  static final _GG1 = _FF1;

  /// GG2 Function
  static int _GG2(int X, int Y, int Z) => (X & Y) | ((~X) & Z);

  /// P0 Function
  static int _P0(int X) => X ^ rotl32(X, 9) ^ rotl32(X, 17);

  /// P1 Function
  static int _P1(int X) => X ^ rotl32(X, 15) ^ rotl32(X, 23);

  @override
  int get byteLength => 64;
}
