Skip to content
This repository has been archived by the owner on Dec 23, 2020. It is now read-only.

Add Blake2b #164

Merged
merged 3 commits into from
Feb 20, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ As of the last release, the following algorithms are implemented:
* PKCS7

**Digests:**
* Blake2b
* MD2
* MD4
* MD5
Expand Down
11 changes: 11 additions & 0 deletions benchmark/digests/blake2b_benchmark.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) 2017-present, the authors of the Pointy Castle project
// This library is dually licensed under LGPL 3 and MPL 2.0.
// See file LICENSE for more information.

library pointycastle.benchmark.digests.blake2b_benchmark;

import "../benchmark/digest_benchmark.dart";

main() {
new DigestBenchmark("Blake2b").report();
}
276 changes: 276 additions & 0 deletions lib/digests/blake2b.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
// Copyright (c) 2015-present, the authors of the Pointy Castle project
// This library is dually licensed under LGPL 3 and MPL 2.0.
// See file LICENSE for more information.

library pointycastle.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 FACTORY_CONFIG =
new 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 = new Register64List(16); // In the Blake2b paper it is called: v
Register64List _chainValue; // state vector, in the Blake2b paper it is called: h

final _t0 = new Register64(); // holds last significant bits, counter (counts bytes)
final _t1 = new Register64(); // counter: Length up to 2^128 are supported
final _f0 = new Register64(); // finalization flag, for last block: ~0L

Blake2bDigest(
{int digestSize = 64,
Uint8List key = null,
Uint8List salt = null,
Uint8List personalization = null}) {
_buffer = new Uint8List(_blockSize);

if (digestSize < 1 || digestSize > 64) {
throw new ArgumentError("Invalid digest length (required: 1 - 64)");
}
_digestLength = digestSize;
if (salt != null) {
if (salt.length != 16) throw new ArgumentError("salt length must be exactly 16 bytes");
_salt = new Uint8List.fromList(salt);
}
if (personalization != null) {
if (personalization.length != 16)
throw new ArgumentError("personalization length must be exactly 16 bytes");
_personalization = new Uint8List.fromList(personalization);
}
if (key != null) {
if (key.length > 64) throw new ArgumentError("Keys > 64 are not supported");
_key = new Uint8List.fromList(key);

_keyLength = key.length;
_buffer.setAll(0, key);
_bufferPos = _blockSize;
}
init();
}

String get algorithmName => "Blake2b";
int get digestSize => _digestLength;

void init() {
if (_chainValue == null) {
_chainValue = new Register64List(8);
_chainValue[0]
..set(_blake2b_IV[0])
..xor(new Register64(digestSize | (_keyLength << 8) | 0x1010000));
_chainValue[1].set(_blake2b_IV[1]);
_chainValue[2].set(_blake2b_IV[2]);

_chainValue[3].set(_blake2b_IV[3]);

_chainValue[4].set(_blake2b_IV[4]);
_chainValue[5].set(_blake2b_IV[5]);
if (_salt != null) {
_chainValue[4].xor(new Register64()..unpack(_salt, 0, Endian.little));
_chainValue[5].xor(new Register64()..unpack(_salt, 8, Endian.little));
}

_chainValue[6].set(_blake2b_IV[6]);
_chainValue[7].set(_blake2b_IV[7]);
if (_personalization != null) {
_chainValue[6].xor(new Register64()..unpack(_personalization, 0, Endian.little));
_chainValue[7].xor(new Register64()..unpack(_personalization, 8, Endian.little));
}
}
}

void _initializeInternalState() {
_internalState.setRange(0, _chainValue.length, _chainValue);
_internalState.setRange(_chainValue.length, _chainValue.length + 4, _blake2b_IV);
_internalState[12]
..set(_t0)
..xor(_blake2b_IV[4]);
_internalState[13]
..set(_t1)
..xor(_blake2b_IV[5]);
_internalState[14]
..set(_f0)
..xor(_blake2b_IV[6]);
_internalState[15]..set(_blake2b_IV[7]); // ^ f1 with f1 = 0
}

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;
}
}

void update(Uint8List inp, int inpOff, int len) {
if(inp == null || len == 0)
return;

int 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(inp, 0);
_bufferPos = 0;
_buffer.fillRange(0, _buffer.length, 0); // clear buffer
} else {
_buffer.setRange(_bufferPos, _bufferPos + len, inp, inpOff);
_bufferPos += len;
return;
}
}

int msgPos;
int 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;
}

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 = new 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;
}

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 = new 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[_blake2b_sigma[round][0]], _m[_blake2b_sigma[round][1]], 0, 4, 8, 12);
G(_m[_blake2b_sigma[round][2]], _m[_blake2b_sigma[round][3]], 1, 5, 9, 13);
G(_m[_blake2b_sigma[round][4]], _m[_blake2b_sigma[round][5]], 2, 6, 10, 14);
G(_m[_blake2b_sigma[round][6]], _m[_blake2b_sigma[round][7]], 3, 7, 11, 15);
G(_m[_blake2b_sigma[round][8]], _m[_blake2b_sigma[round][9]], 0, 5, 10, 15);
G(_m[_blake2b_sigma[round][10]], _m[_blake2b_sigma[round][11]], 1, 6, 11, 12);
G(_m[_blake2b_sigma[round][12]], _m[_blake2b_sigma[round][13]], 2, 7, 8, 13);
G(_m[_blake2b_sigma[round][14]], _m[_blake2b_sigma[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 = new 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);
}
}

// Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
// The same as SHA-512 IV.
final _blake2b_IV = new Register64List.from([
[0x6a09e667, 0xf3bcc908],
[0xbb67ae85, 0x84caa73b],
[0x3c6ef372, 0xfe94f82b],
[0xa54ff53a, 0x5f1d36f1],
[0x510e527f, 0xade682d1],
[0x9b05688c, 0x2b3e6c1f],
[0x1f83d9ab, 0xfb41bd6b],
[0x5be0cd19, 0x137e2179],
]);

final _blake2b_sigma = [
[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],
];
1 change: 1 addition & 0 deletions lib/export.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export "package:pointycastle/block/modes/ofb.dart";
export "package:pointycastle/block/modes/sic.dart";

// digests
export "package:pointycastle/digests/blake2b.dart";
export "package:pointycastle/digests/md2.dart";
export "package:pointycastle/digests/md4.dart";
export "package:pointycastle/digests/md5.dart";
Expand Down
2 changes: 2 additions & 0 deletions lib/src/registry/registration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:pointycastle/block/modes/ecb.dart';
import 'package:pointycastle/block/modes/gctr.dart';
import 'package:pointycastle/block/modes/ofb.dart';
import 'package:pointycastle/block/modes/sic.dart';
import 'package:pointycastle/digests/blake2b.dart';
import 'package:pointycastle/digests/md2.dart';
import 'package:pointycastle/digests/md4.dart';
import 'package:pointycastle/digests/md5.dart';
Expand Down Expand Up @@ -118,6 +119,7 @@ void _registerBlockCiphers(FactoryRegistry registry) {
}

void _registerDigests(FactoryRegistry registry) {
registry.register(Blake2bDigest.FACTORY_CONFIG);
registry.register(MD2Digest.FACTORY_CONFIG);
registry.register(MD4Digest.FACTORY_CONFIG);
registry.register(MD5Digest.FACTORY_CONFIG);
Expand Down
16 changes: 12 additions & 4 deletions lib/src/ufixnum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@ class Register64 {
}
}

void sumReg(Register64 y) {
int slo32 = (_lo32 + y._lo32);
_lo32 = (slo32 & _MASK_32);
int carry = ((slo32 != _lo32) ? 1 : 0);
_hi32 = ((_hi32 + y._hi32 + carry) & _MASK_32);
}

void sub(dynamic y) {
// TODO: optimize sub() ???
sum(new Register64(y)..neg());
Expand Down Expand Up @@ -429,7 +436,7 @@ class Register64 {
}

/**
* Unpacks a 32 bit integer from a byte buffer. The [inp] parameter can be an [Uint8List] or a
* Unpacks a 64 bit integer from a byte buffer. The [inp] parameter can be an [Uint8List] or a
* [ByteData] if you will run it several times against the same buffer and want faster execution.
*/
void unpack(dynamic inp, int offset, Endian endian) {
Expand Down Expand Up @@ -485,9 +492,10 @@ class Register64List {
}
}

void setRange(int start, int end, Register64List list) {
for (var i = start; i < end; i++) {
_list[i].set(list[i]);
void setRange(int start, int end, Register64List list, [int skipCount = 0]) {
var length = end - start;
for (var i = 0; i < length; i++) {
_list[start + i].set(list[skipCount + i]);
}
}

Expand Down
2 changes: 2 additions & 0 deletions test/all_tests_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "asymmetric/pkcs1_test.dart" as pkcs1_test;
import "asymmetric/oaep_test.dart" as oaep_test;
import "asymmetric/rsa_test.dart" as rsa_test;
import "block/aes_fast_test.dart" as aes_fast_test;
import "digests/blake2b_test.dart" as blake2b_test;
import "digests/md2_test.dart" as md2_test;
import "digests/md4_test.dart" as md4_test;
import "digests/md5_test.dart" as md5_test;
Expand Down Expand Up @@ -49,6 +50,7 @@ void main() {
oaep_test.main();
rsa_test.main();
aes_fast_test.main();
blake2b_test.main();
md2_test.main();
md4_test.main();
md5_test.main();
Expand Down
Loading