sm3.js 9.81 KB
/* eslint-disable no-bitwise, no-mixed-operators, class-methods-use-this, camelcase */
const {BigInteger} = require('jsbn')
const _ = require('./utils')

const copyArray = function (sourceArray, sourceIndex, destinationArray, destinationIndex, length) {
  for (let i = 0; i < length; i++) {
    destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i]
  }
}

const Int32 = {
  minValue: -0b10000000000000000000000000000000,
  maxValue: 0b1111111111111111111111111111111,
  parse(n) {
    if (n < this.minValue) {
      const bigInteger = Number(-n)
      const bigIntegerRadix = bigInteger.toString(2)
      const subBigIntegerRadix = bigIntegerRadix.substr(bigIntegerRadix.length - 31, 31)
      let reBigIntegerRadix = ''
      for (let i = 0; i < subBigIntegerRadix.length; i++) {
        const subBigIntegerRadixItem = subBigIntegerRadix.substr(i, 1)
        reBigIntegerRadix += subBigIntegerRadixItem === '0' ? '1' : '0'
      }
      const result = parseInt(reBigIntegerRadix, 2)
      return (result + 1)
    } else if (n > this.maxValue) {
      const bigInteger = Number(n)
      const bigIntegerRadix = bigInteger.toString(2)
      const subBigIntegerRadix = bigIntegerRadix.substr(bigIntegerRadix.length - 31, 31)
      let reBigIntegerRadix = ''
      for (let i = 0; i < subBigIntegerRadix.length; i++) {
        const subBigIntegerRadixItem = subBigIntegerRadix.substr(i, 1)
        reBigIntegerRadix += subBigIntegerRadixItem === '0' ? '1' : '0'
      }
      const result = parseInt(reBigIntegerRadix, 2)
      return -(result + 1)
    } else {
      return n
    }
  },
  parseByte(n) {
    if (n < 0) {
      const bigInteger = Number(-n)
      const bigIntegerRadix = bigInteger.toString(2)
      const subBigIntegerRadix = bigIntegerRadix.substr(bigIntegerRadix.length - 8, 8)
      let reBigIntegerRadix = ''
      for (let i = 0; i < subBigIntegerRadix.length; i++) {
        const subBigIntegerRadixItem = subBigIntegerRadix.substr(i, 1)
        reBigIntegerRadix += subBigIntegerRadixItem === '0' ? '1' : '0'
      }
      const result = parseInt(reBigIntegerRadix, 2)
      return (result + 1) % 256
    } else if (n > 255) {
      const bigInteger = Number(n)
      const bigIntegerRadix = bigInteger.toString(2)
      return parseInt(bigIntegerRadix.substr(bigIntegerRadix.length - 8, 8), 2)
    } else {
      return n
    }
  }
}

class SM3Digest {
  constructor(...args) {
    this.xBuf = []
    this.xBufOff = 0
    this.byteCount = 0
    this.DIGEST_LENGTH = 32
    this.v0 = [0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
      0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e]
    this.v0 = [0x7380166f, 0x4914b2b9, 0x172442d7, -628488704,
      -1452330820, 0x163138aa, -477237683, -1325724082]
    this.v = new Array(8)
    this.v_ = new Array(8)
    this.X0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    this.X = new Array(68)
    this.xOff = 0
    this.T_00_15 = 0x79cc4519
    this.T_16_63 = 0x7a879d8a
    if (args.length > 0) {
      this.initDigest(args[0])
    } else {
      this.init()
    }
  }

  init() {
    this.xBuf = new Array(4)
    this.reset()
  }

  initDigest(t) {
    this.xBuf = [].concat(t.xBuf)
    this.xBufOff = t.xBufOff
    this.byteCount = t.byteCount
    copyArray(t.X, 0, this.X, 0, t.X.length)
    this.xOff = t.xOff
    copyArray(t.v, 0, this.v, 0, t.v.length)
  }

  getDigestSize() {
    return this.DIGEST_LENGTH
  }

  reset() {
    this.byteCount = 0
    this.xBufOff = 0

    const keys = Object.keys(this.xBuf)
    for (let i = 0, len = keys.length; i < len; i++) this.xBuf[keys[i]] = null

    copyArray(this.v0, 0, this.v, 0, this.v0.length)
    this.xOff = 0
    copyArray(this.X0, 0, this.X, 0, this.X0.length)
  }

  processBlock() {
    let i
    const ww = this.X
    const ww_ = new Array(64)
    for (i = 16; i < 68; i++) {
      ww[i] = this.p1(ww[i - 16] ^ ww[i - 9] ^ (this.rotate(ww[i - 3], 15))) ^ (this.rotate(ww[i - 13], 7)) ^ ww[i - 6]
    }
    for (i = 0; i < 64; i++) {
      ww_[i] = ww[i] ^ ww[i + 4]
    }
    const vv = this.v
    const vv_ = this.v_
    copyArray(vv, 0, vv_, 0, this.v0.length)
    let SS1
    let SS2
    let TT1
    let TT2
    let aaa
    for (i = 0; i < 16; i++) {
      aaa = this.rotate(vv_[0], 12)
      SS1 = Int32.parse(Int32.parse(aaa + vv_[4]) + this.rotate(this.T_00_15, i))
      SS1 = this.rotate(SS1, 7)
      SS2 = SS1 ^ aaa
      TT1 = Int32.parse(Int32.parse(this.ff_00_15(vv_[0], vv_[1], vv_[2]) + vv_[3]) + SS2) + ww_[i]
      TT2 = Int32.parse(Int32.parse(this.gg_00_15(vv_[4], vv_[5], vv_[6]) + vv_[7]) + SS1) + ww[i]
      vv_[3] = vv_[2]
      vv_[2] = this.rotate(vv_[1], 9)
      vv_[1] = vv_[0]
      vv_[0] = TT1
      vv_[7] = vv_[6]
      vv_[6] = this.rotate(vv_[5], 19)
      vv_[5] = vv_[4]
      vv_[4] = this.p0(TT2)
    }
    for (i = 16; i < 64; i++) {
      aaa = this.rotate(vv_[0], 12)
      SS1 = Int32.parse(Int32.parse(aaa + vv_[4]) + this.rotate(this.T_16_63, i))
      SS1 = this.rotate(SS1, 7)
      SS2 = SS1 ^ aaa
      TT1 = Int32.parse(Int32.parse(this.ff_16_63(vv_[0], vv_[1], vv_[2]) + vv_[3]) + SS2) + ww_[i]
      TT2 = Int32.parse(Int32.parse(this.gg_16_63(vv_[4], vv_[5], vv_[6]) + vv_[7]) + SS1) + ww[i]
      vv_[3] = vv_[2]
      vv_[2] = this.rotate(vv_[1], 9)
      vv_[1] = vv_[0]
      vv_[0] = TT1
      vv_[7] = vv_[6]
      vv_[6] = this.rotate(vv_[5], 19)
      vv_[5] = vv_[4]
      vv_[4] = this.p0(TT2)
    }
    for (i = 0; i < 8; i++) {
      vv[i] ^= Int32.parse(vv_[i])
    }
    this.xOff = 0
    copyArray(this.X0, 0, this.X, 0, this.X0.length)
  }

  processWord(in_Renamed, inOff) {
    let n = in_Renamed[inOff] << 24
    n |= (in_Renamed[++inOff] & 0xff) << 16
    n |= (in_Renamed[++inOff] & 0xff) << 8
    n |= (in_Renamed[++inOff] & 0xff)
    this.X[this.xOff] = n
    if (++this.xOff === 16) {
      this.processBlock()
    }
  }

  processLength(bitLength) {
    if (this.xOff > 14) {
      this.processBlock()
    }
    this.X[14] = (this.urShiftLong(bitLength, 32))
    this.X[15] = (bitLength & (0xffffffff))
  }

  intToBigEndian(n, bs, off) {
    bs[off] = Int32.parseByte(this.urShift(n, 24)) & 0xff
    bs[++off] = Int32.parseByte(this.urShift(n, 16)) & 0xff
    bs[++off] = Int32.parseByte(this.urShift(n, 8)) & 0xff
    bs[++off] = Int32.parseByte(n) & 0xff
  }

  doFinal(out_Renamed, outOff) {
    this.finish()
    for (let i = 0; i < 8; i++) {
      this.intToBigEndian(this.v[i], out_Renamed, outOff + i * 4)
    }
    this.reset()
    return this.DIGEST_LENGTH
  }

  update(input) {
    this.xBuf[this.xBufOff++] = input
    if (this.xBufOff === this.xBuf.length) {
      this.processWord(this.xBuf, 0)
      this.xBufOff = 0
    }
    this.byteCount++
  }

  blockUpdate(input, inOff, length) {
    while ((this.xBufOff !== 0) && (length > 0)) {
      this.update(input[inOff])
      inOff++
      length--
    }
    while (length > this.xBuf.length) {
      this.processWord(input, inOff)
      inOff += this.xBuf.length
      length -= this.xBuf.length
      this.byteCount += this.xBuf.length
    }
    while (length > 0) {
      this.update(input[inOff])
      inOff++
      length--
    }
  }

  finish() {
    const bitLength = (this.byteCount << 3)
    this.update((128))
    while (this.xBufOff !== 0) this.update((0))
    this.processLength(bitLength)
    this.processBlock()
  }

  rotate(x, n) {
    return (x << n) | (this.urShift(x, (32 - n)))
  }

  p0(X) {
    return ((X) ^ this.rotate((X), 9) ^ this.rotate((X), 17))
  }

  p1(X) {
    return ((X) ^ this.rotate((X), 15) ^ this.rotate((X), 23))
  }

  ff_00_15(X, Y, Z) {
    return (X ^ Y ^ Z)
  }

  ff_16_63(X, Y, Z) {
    return ((X & Y) | (X & Z) | (Y & Z))
  }

  gg_00_15(X, Y, Z) {
    return (X ^ Y ^ Z)
  }

  gg_16_63(X, Y, Z) {
    return ((X & Y) | (~X & Z))
  }

  urShift(number, bits) {
    if (number > Int32.maxValue || number < Int32.minValue) {
      number = Int32.parse(number)
    }
    return number >>> bits
  }

  urShiftLong(number, bits) {
    let returnV
    const big = new BigInteger()
    big.fromInt(number)
    if (big.signum() >= 0) {
      returnV = big.shiftRight(bits).intValue()
    } else {
      const bigAdd = new BigInteger()
      bigAdd.fromInt(2)
      const shiftLeftBits = ~bits
      let shiftLeftNumber = ''
      if (shiftLeftBits < 0) {
        const shiftRightBits = 64 + shiftLeftBits
        for (let i = 0; i < shiftRightBits; i++) {
          shiftLeftNumber += '0'
        }
        const shiftLeftNumberBigAdd = new BigInteger()
        shiftLeftNumberBigAdd.fromInt(number >> bits)
        const shiftLeftNumberBig = new BigInteger('10' + shiftLeftNumber, 2)
        shiftLeftNumber = shiftLeftNumberBig.toRadix(10)
        const r = shiftLeftNumberBig.add(shiftLeftNumberBigAdd)
        returnV = r.toRadix(10)
      } else {
        shiftLeftNumber = bigAdd.shiftLeft((~bits)).intValue()
        returnV = (number >> bits) + shiftLeftNumber
      }
    }
    return returnV
  }

  getZ(g, publicKey) {
    const userId = _.parseUtf8StringToHex('1234567812345678')
    const len = userId.length * 4
    this.update((len >> 8 & 0x00ff))
    this.update((len & 0x00ff))
    const userIdWords = _.hexToArray(userId)
    this.blockUpdate(userIdWords, 0, userIdWords.length)
    const aWords = _.hexToArray(g.curve.a.toBigInteger().toRadix(16))
    const bWords = _.hexToArray(g.curve.b.toBigInteger().toRadix(16))
    const gxWords = _.hexToArray(g.getX().toBigInteger().toRadix(16))
    const gyWords = _.hexToArray(g.getY().toBigInteger().toRadix(16))
    const pxWords = _.hexToArray(publicKey.substr(0, 64))
    const pyWords = _.hexToArray(publicKey.substr(64, 64))
    this.blockUpdate(aWords, 0, aWords.length)
    this.blockUpdate(bWords, 0, bWords.length)
    this.blockUpdate(gxWords, 0, gxWords.length)
    this.blockUpdate(gyWords, 0, gyWords.length)
    this.blockUpdate(pxWords, 0, pxWords.length)
    this.blockUpdate(pyWords, 0, pyWords.length)
    const md = new Array(this.getDigestSize())
    this.doFinal(md, 0)
    return md
  }
}

module.exports = SM3Digest