// This file has been migrated. library impl.digest.blake2b; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; import 'package:pointycastle/src/impl/base_digest.dart'; import 'package:pointycastle/src/registry/registry.dart'; import 'package:pointycastle/src/ufixnum.dart'; class Blake2bDigest extends BaseDigest implements Digest { static final FactoryConfig factoryConfig = StaticFactoryConfig(Digest, 'Blake2b', () => Blake2bDigest()); static const _rounds = 12; static const _blockSize = 128; int _digestLength = 64; int _keyLength = 0; Uint8List? _salt; Uint8List? _personalization; Uint8List? _key; Uint8List? _buffer; // Position of last inserted byte: int _bufferPos = 0; // a value from 0 up to 128 final _internalState = Register64List(16); // In the Blake2b paper it is called: v Register64List? _chainValue; // state vector, in the Blake2b paper it is called: h final _t0 = Register64(); // holds last significant bits, counter (counts bytes) final _t1 = Register64(); // counter: Length up to 2^128 are supported final _f0 = Register64(); // finalization flag, for last block: ~0L Blake2bDigest( {int digestSize = 64, Uint8List? key, Uint8List? salt, Uint8List? personalization}) { _buffer = Uint8List(_blockSize); if (digestSize < 1 || digestSize > 64) { throw ArgumentError('Invalid digest length (required: 1 - 64)'); } _digestLength = digestSize; if (salt != null) { if (salt.length != 16) { throw ArgumentError('salt length must be exactly 16 bytes'); } _salt = Uint8List.fromList(salt); } if (personalization != null) { if (personalization.length != 16) { throw ArgumentError('personalization length must be exactly 16 bytes'); } _personalization = Uint8List.fromList(personalization); } if (key != null) { if (key.length > 64) throw ArgumentError('Keys > 64 are not supported'); _key = Uint8List.fromList(key); _keyLength = key.length; _buffer!.setAll(0, key); _bufferPos = _blockSize; } init(); } @override String get algorithmName => 'Blake2b'; @override int get digestSize => _digestLength; void init() { if (_chainValue == null) { _chainValue = Register64List(8); _chainValue![0] ..set(_blake2bIV[0]) ..xor(Register64(digestSize | (_keyLength << 8) | 0x1010000)); _chainValue![1].set(_blake2bIV[1]); _chainValue![2].set(_blake2bIV[2]); _chainValue![3].set(_blake2bIV[3]); _chainValue![4].set(_blake2bIV[4]); _chainValue![5].set(_blake2bIV[5]); if (_salt != null) { _chainValue![4].xor(Register64()..unpack(_salt, 0, Endian.little)); _chainValue![5].xor(Register64()..unpack(_salt, 8, Endian.little)); } _chainValue![6].set(_blake2bIV[6]); _chainValue![7].set(_blake2bIV[7]); if (_personalization != null) { _chainValue![6] .xor(Register64()..unpack(_personalization, 0, Endian.little)); _chainValue![7] .xor(Register64()..unpack(_personalization, 8, Endian.little)); } } } void _initializeInternalState() { _internalState.setRange(0, _chainValue!.length, _chainValue!); _internalState.setRange( _chainValue!.length, _chainValue!.length + 4, _blake2bIV); _internalState[12] ..set(_t0) ..xor(_blake2bIV[4]); _internalState[13] ..set(_t1) ..xor(_blake2bIV[5]); _internalState[14] ..set(_f0) ..xor(_blake2bIV[6]); _internalState[15].set(_blake2bIV[7]); // ^ f1 with f1 = 0 } @override void updateByte(int inp) { if (_bufferPos == _blockSize) { // full buffer _t0.sum(_blockSize); // This requires hashing > 2^64 bytes which is impossible for the forseeable future. // So _t1 is untested dead code, but I've left it in because it is in the source library. if (_t0.lo32 == 0 && _t0.hi32 == 0) _t1.sum(1); _compress(_buffer, 0); _buffer!.fillRange(0, _buffer!.length, 0); // clear buffer _buffer![0] = inp; _bufferPos = 1; } else { _buffer![_bufferPos] = inp; ++_bufferPos; } } @override void update(Uint8List inp, int inpOff, int len) { if (len == 0) return; var remainingLength = 0; if (_bufferPos != 0) { remainingLength = _blockSize - _bufferPos; if (remainingLength < len) { _buffer! .setRange(_bufferPos, _bufferPos + remainingLength, inp, inpOff); _t0.sum(_blockSize); if (_t0.lo32 == 0 && _t0.hi32 == 0) _t1.sum(1); _compress(_buffer, 0); _bufferPos = 0; _buffer!.fillRange(0, _buffer!.length, 0); // clear buffer } else { _buffer!.setRange(_bufferPos, _bufferPos + len, inp, inpOff); _bufferPos += len; return; } } int msgPos; var blockWiseLastPos = inpOff + len - _blockSize; for (msgPos = inpOff + remainingLength; msgPos < blockWiseLastPos; msgPos += _blockSize) { _t0.sum(_blockSize); if (_t0.lo32 == 0 && _t0.hi32 == 0) _t1.sum(1); _compress(inp, msgPos); } _buffer!.setRange(0, inpOff + len - msgPos, inp, msgPos); _bufferPos += inpOff + len - msgPos; } @override int doFinal(Uint8List out, int outOff) { _f0.set(0xFFFFFFFF, 0xFFFFFFFF); _t0.sum(_bufferPos); if (_bufferPos > 0 && _t0.lo32 == 0 && _t0.hi32 == 0) _t1.sum(1); _compress(_buffer, 0); _buffer!.fillRange(0, _buffer!.length, 0); // clear buffer _internalState.fillRange(0, _internalState.length, 0); final packedValue = Uint8List(8); final packedValueData = packedValue.buffer.asByteData(); for (var i = 0; i < _chainValue!.length && (i * 8 < _digestLength); ++i) { _chainValue![i].pack(packedValueData, 0, Endian.little); final start = outOff + i * 8; if (i * 8 < _digestLength - 8) { out.setRange(start, start + 8, packedValue); } else { out.setRange(start, start + _digestLength - (i * 8), packedValue); } } _chainValue!.fillRange(0, _chainValue!.length, 0); reset(); return _digestLength; } @override void reset() { _bufferPos = 0; _f0.set(0); _t0.set(0); _t1.set(0); _chainValue = null; _buffer!.fillRange(0, _buffer!.length, 0); if (_key != null) { _buffer!.setAll(0, _key!); _bufferPos = _blockSize; } init(); } // This variable is faster as a class member. final _m = Register64List(16); void _compress(Uint8List? message, int messagePos) { _initializeInternalState(); for (var j = 0; j < 16; ++j) { _m[j].unpack(message, messagePos + j * 8, Endian.little); } for (var round = 0; round < _rounds; ++round) { G(_m[_blake2bSigma[round][0]], _m[_blake2bSigma[round][1]], 0, 4, 8, 12); G(_m[_blake2bSigma[round][2]], _m[_blake2bSigma[round][3]], 1, 5, 9, 13); G(_m[_blake2bSigma[round][4]], _m[_blake2bSigma[round][5]], 2, 6, 10, 14); G(_m[_blake2bSigma[round][6]], _m[_blake2bSigma[round][7]], 3, 7, 11, 15); G(_m[_blake2bSigma[round][8]], _m[_blake2bSigma[round][9]], 0, 5, 10, 15); G(_m[_blake2bSigma[round][10]], _m[_blake2bSigma[round][11]], 1, 6, 11, 12); G(_m[_blake2bSigma[round][12]], _m[_blake2bSigma[round][13]], 2, 7, 8, 13); G(_m[_blake2bSigma[round][14]], _m[_blake2bSigma[round][15]], 3, 4, 9, 14); } for (var offset = 0; offset < _chainValue!.length; ++offset) { _chainValue![offset] ..xor(_internalState[offset]) ..xor(_internalState[offset + 8]); } } void G(Register64 m1, Register64 m2, int posA, int posB, int posC, int posD) { // This variable is faster as a local. The allocation is probably sunk. final r = Register64(); _internalState[posA].sumReg(r ..set(_internalState[posB]) ..sumReg(m1)); _internalState[posD] ..xor(_internalState[posA]) ..rotr(32); _internalState[posC].sumReg(_internalState[posD]); _internalState[posB] ..xor(_internalState[posC]) ..rotr(24); _internalState[posA].sumReg(r ..set(_internalState[posB]) ..sumReg(m2)); _internalState[posD] ..xor(_internalState[posA]) ..rotr(16); _internalState[posC].sumReg(_internalState[posD]); _internalState[posB] ..xor(_internalState[posC]) ..rotr(63); } @override int get byteLength => 128; } // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19. // The same as SHA-512 IV. final _blake2bIV = Register64List.from([ [0x6a09e667, 0xf3bcc908], [0xbb67ae85, 0x84caa73b], [0x3c6ef372, 0xfe94f82b], [0xa54ff53a, 0x5f1d36f1], [0x510e527f, 0xade682d1], [0x9b05688c, 0x2b3e6c1f], [0x1f83d9ab, 0xfb41bd6b], [0x5be0cd19, 0x137e2179], ]); final _blake2bSigma = [ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], ];