306 lines
9.4 KiB
Dart
306 lines
9.4 KiB
Dart
// 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],
|
|
];
|