blob: 842bfd4909f7662d47645e5f66f08d2f07c8fd92 [file] [log] [blame] [edit]
// 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],
];