add dependencies
This commit is contained in:
parent
6b82890b13
commit
de6c8c4e36
1560 changed files with 309395 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.venv
|
||||||
11
adaptive_number/LICENSE
Normal file
11
adaptive_number/LICENSE
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
Copyright 2021 Philipp Sessler
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
6
adaptive_number/lib/adaptive_number.dart
Normal file
6
adaptive_number/lib/adaptive_number.dart
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
/// Support for doing something awesome.
|
||||||
|
///
|
||||||
|
/// More dartdocs go here.
|
||||||
|
library adaptive_number;
|
||||||
|
|
||||||
|
export 'src/number.dart';
|
||||||
120
adaptive_number/lib/src/int.dart
Normal file
120
adaptive_number/lib/src/int.dart
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
import 'package:adaptive_number/src/number.dart';
|
||||||
|
|
||||||
|
class NumberInt implements Number {
|
||||||
|
final int _value;
|
||||||
|
|
||||||
|
NumberInt(this._value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator +(Number value) {
|
||||||
|
return NumberInt(_value + (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator -(Number value) {
|
||||||
|
return NumberInt(_value - (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator -() {
|
||||||
|
return NumberInt(-(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator *(Number value) {
|
||||||
|
return NumberInt(_value * (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator &(Number value) {
|
||||||
|
return NumberInt(_value & (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator >>(int value) {
|
||||||
|
return NumberInt(_value >> value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator <<(int value) {
|
||||||
|
return NumberInt(_value << value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator ^(Number value) {
|
||||||
|
return NumberInt(_value ^ (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator |(Number value) {
|
||||||
|
return NumberInt(_value | (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator <(Number value) {
|
||||||
|
return (intValue < value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator <=(Number value) {
|
||||||
|
return (intValue <= value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator >(Number value) {
|
||||||
|
return (intValue > value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator >=(Number value) {
|
||||||
|
return (intValue >= value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt operator %(Number value) {
|
||||||
|
return NumberInt(_value % (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Number operator ~/(Number value) {
|
||||||
|
return NumberInt(_value ~/ (value.val as int));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get val => _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get intValue => _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is NumberInt &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
_value == other._value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => _value.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return _value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toRadixString(int radix) {
|
||||||
|
return _value.toRadixString(radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt abs() {
|
||||||
|
return NumberInt(_value.abs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int compareTo(Number other) {
|
||||||
|
return _value.compareTo(other.intValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Number createNumber(int val) => NumberInt(val);
|
||||||
121
adaptive_number/lib/src/int64.dart
Normal file
121
adaptive_number/lib/src/int64.dart
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
import 'package:adaptive_number/src/number.dart';
|
||||||
|
import 'package:fixnum/fixnum.dart';
|
||||||
|
|
||||||
|
class NumberInt64 implements Number {
|
||||||
|
final Int64 _value;
|
||||||
|
|
||||||
|
NumberInt64(this._value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator +(Number value) {
|
||||||
|
return NumberInt64(_value + (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator -(Number value) {
|
||||||
|
return NumberInt64(_value - (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator -() {
|
||||||
|
return NumberInt64(-(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator *(Number value) {
|
||||||
|
return NumberInt64(_value * (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator &(Number value) {
|
||||||
|
return NumberInt64(_value & (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator >>(int value) {
|
||||||
|
return NumberInt64(_value >> value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator <<(int value) {
|
||||||
|
return NumberInt64(_value << value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator ^(Number value) {
|
||||||
|
return NumberInt64(_value ^ (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator |(Number value) {
|
||||||
|
return NumberInt64(_value | (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator <(Number value) {
|
||||||
|
return (intValue < value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator <=(Number value) {
|
||||||
|
return (intValue <= value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator >(Number value) {
|
||||||
|
return (intValue > value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator >=(Number value) {
|
||||||
|
return (intValue >= value.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 operator %(Number value) {
|
||||||
|
return NumberInt64(_value % (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Number operator ~/(Number value) {
|
||||||
|
return NumberInt64(_value ~/ (value.val as Int64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Int64 get val => _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get intValue => _value.toInt();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is NumberInt64 &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
_value == other._value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => _value.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return _value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toRadixString(int radix) {
|
||||||
|
return _value.toRadixString(radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
NumberInt64 abs() {
|
||||||
|
return NumberInt64(_value.abs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int compareTo(Number value) {
|
||||||
|
return _value.compareTo(value.val as Int64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Number createNumber(int val) => NumberInt64(Int64(val));
|
||||||
78
adaptive_number/lib/src/number.dart
Normal file
78
adaptive_number/lib/src/number.dart
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import 'package:adaptive_number/src/stub.dart'
|
||||||
|
// ignore: uri_does_not_exist
|
||||||
|
if (dart.library.io) 'package:adaptive_number/src/int.dart'
|
||||||
|
// ignore: uri_does_not_exist
|
||||||
|
if (dart.library.html) 'package:adaptive_number/src/int64.dart';
|
||||||
|
|
||||||
|
abstract class Number {
|
||||||
|
static Number zero = Number(0);
|
||||||
|
static Number one = Number(1);
|
||||||
|
static Number two = Number(2);
|
||||||
|
|
||||||
|
factory Number(int val) => createNumber(val);
|
||||||
|
|
||||||
|
dynamic get val;
|
||||||
|
|
||||||
|
/// Returns the value as int (caution: May overflow on JS runtimes)
|
||||||
|
int get intValue;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode;
|
||||||
|
|
||||||
|
/// Addition operator.
|
||||||
|
Number operator +(Number value);
|
||||||
|
|
||||||
|
/// Subtraction operator.
|
||||||
|
Number operator -(Number value);
|
||||||
|
|
||||||
|
/// Negate operator.
|
||||||
|
Number operator -();
|
||||||
|
|
||||||
|
/// Multiplication operator.
|
||||||
|
Number operator *(Number value);
|
||||||
|
|
||||||
|
/// Bitwise and operator.
|
||||||
|
Number operator &(Number value);
|
||||||
|
|
||||||
|
/// Right bit-shift operator.
|
||||||
|
Number operator >>(int value);
|
||||||
|
|
||||||
|
/// Left bit-shift operator.
|
||||||
|
Number operator <<(int value);
|
||||||
|
|
||||||
|
/// Bitwise xor operator.
|
||||||
|
Number operator ^(Number value);
|
||||||
|
|
||||||
|
/// Bitwise or operator.
|
||||||
|
Number operator |(Number value);
|
||||||
|
|
||||||
|
/// Relational less than operator.
|
||||||
|
bool operator <(Number value);
|
||||||
|
|
||||||
|
/// Relational less than or equal to operator.
|
||||||
|
bool operator <=(Number value);
|
||||||
|
|
||||||
|
/// Relational greater than operator.
|
||||||
|
bool operator >(Number value);
|
||||||
|
|
||||||
|
/// Relational greater than or equal to operator.
|
||||||
|
bool operator >=(Number value);
|
||||||
|
|
||||||
|
/// Euclidean modulo operator.
|
||||||
|
Number operator %(Number value);
|
||||||
|
|
||||||
|
/// Truncating division operator.
|
||||||
|
Number operator ~/(Number value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString();
|
||||||
|
|
||||||
|
/// Returns a string representing the value of this integer in the given radix.
|
||||||
|
String toRadixString(int radix);
|
||||||
|
|
||||||
|
/// Returns the absolute value of this integer.
|
||||||
|
Number abs();
|
||||||
|
|
||||||
|
// Compares this to `other`
|
||||||
|
int compareTo(Number other);
|
||||||
|
}
|
||||||
4
adaptive_number/lib/src/stub.dart
Normal file
4
adaptive_number/lib/src/stub.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import 'package:adaptive_number/src/number.dart';
|
||||||
|
|
||||||
|
Number createNumber(int val) => throw UnsupportedError(
|
||||||
|
'Cannot create a Number without package dart.library.io or dart.library.html being available');
|
||||||
17
adaptive_number/pubspec.yaml
Normal file
17
adaptive_number/pubspec.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
name: adaptive_number
|
||||||
|
version: 1.0.0
|
||||||
|
|
||||||
|
description: >-
|
||||||
|
Library providing an adaptive number implementation. On JS runtimes, a 64-bit signed fixed-width integer will be used and
|
||||||
|
for all other platforms the default Dart int data type.
|
||||||
|
homepage: https://github.com/lemoony/adaptive_number_dart
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
fixnum: ^1.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
pedantic: ^1.10.0
|
||||||
|
test: ^1.16.0
|
||||||
128
adaptive_number/test/adaptive_number_test.dart
Normal file
128
adaptive_number/test/adaptive_number_test.dart
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import 'package:adaptive_number/adaptive_number.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
// Since both implementations of Number only forward most method call,
|
||||||
|
// tests are kept minimal and should mainly concentrate on verifying that no
|
||||||
|
// wrong operator and/or method is being called because of a mix up
|
||||||
|
void main() {
|
||||||
|
group('utility functions', () {
|
||||||
|
test('retrieve value as int', () {
|
||||||
|
expect(Number.zero.intValue, 0);
|
||||||
|
expect(Number.one.intValue, 1);
|
||||||
|
expect(Number.two.intValue, 2);
|
||||||
|
expect(Number(42).intValue, 42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('other overwrites', () {
|
||||||
|
test('hash code', () {
|
||||||
|
expect(Number.zero.hashCode, 0.hashCode);
|
||||||
|
expect(Number.one.hashCode, 1.hashCode);
|
||||||
|
expect(Number.two.hashCode, 2.hashCode);
|
||||||
|
expect(Number(42).hashCode, 42);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('to string', () {
|
||||||
|
expect(Number.one.toString(), '1');
|
||||||
|
expect((-Number.one).toString(), '-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toRadixString', () {
|
||||||
|
expect(Number(42).toRadixString(2), '101010');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('abs', () {
|
||||||
|
expect(Number(-42).abs(), Number(42));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('compare to', () {
|
||||||
|
expect(Number.one.compareTo(Number.two), -1);
|
||||||
|
expect(Number.two.compareTo(Number.one), 1);
|
||||||
|
expect(Number.one.compareTo(Number.one), 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('operator tests', () {
|
||||||
|
test('add', () {
|
||||||
|
expect(Number.one + Number.two, Number(3));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('subtract', () {
|
||||||
|
expect(Number.two - Number.one, Number.one);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('invert', () {
|
||||||
|
expect((-Number.one).intValue, -1);
|
||||||
|
expect((-Number.zero).intValue, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('multiply', () {
|
||||||
|
expect(Number.two * Number(3), Number(6));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('& bitwise and', () {
|
||||||
|
expect(Number(1234) & Number(9876), Number(1168));
|
||||||
|
expect(Number(-1234) & Number(9876), Number(8708));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('>> bitwise shift right', () {
|
||||||
|
expect(Number(23423) >> 8, Number(91));
|
||||||
|
expect(Number(-87653) >> 12, Number(-22));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('<< bitwise shift left', () {
|
||||||
|
expect(Number(23423) << 8, Number(5996288));
|
||||||
|
expect(Number(-87653) << 12, Number(-359026688));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('^ bitwise xor', () {
|
||||||
|
expect(Number(1234) ^ Number(9876), Number(8774));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('| bitwise or', () {
|
||||||
|
expect(Number(1234) | Number(9876), Number(9942));
|
||||||
|
expect(Number(-1234) | Number(9876), Number(-66));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('< less than', () {
|
||||||
|
expect(Number.one < Number.two, isTrue);
|
||||||
|
expect(Number.two < Number.one, isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('< less than or equal', () {
|
||||||
|
expect(Number.two <= Number.one, isFalse);
|
||||||
|
expect(Number.one <= Number.two, isTrue);
|
||||||
|
expect(Number.two <= Number.two, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('< greater than', () {
|
||||||
|
expect(Number.two > Number.one, isTrue);
|
||||||
|
expect(Number.one > Number.two, isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('< greater than or equal', () {
|
||||||
|
expect(Number.two >= Number.one, isTrue);
|
||||||
|
expect(Number.one >= Number.two, isFalse);
|
||||||
|
expect(Number.two >= Number.two, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('modulo', () {
|
||||||
|
expect(Number.two % Number.one, Number.zero);
|
||||||
|
expect(Number(3) % Number.one, Number.zero);
|
||||||
|
expect(Number.one % Number.two, Number.one);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('truncating division', () {
|
||||||
|
expect(Number(1000) ~/ Number(-3), Number(-333));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('equals', () {
|
||||||
|
expect(Number(0) == Number.zero, isTrue);
|
||||||
|
expect(Number(1) == Number.one, isTrue);
|
||||||
|
expect(Number(2) == Number.two, isTrue);
|
||||||
|
|
||||||
|
expect(Number(1) == Number.zero, isFalse);
|
||||||
|
expect(Number.one == Number.zero, isFalse);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
14
config.lock.yaml
Normal file
14
config.lock.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
adaptive_number: ea9178fdd4d82ac45cf0ec966ac870dae661124f
|
||||||
|
dots_indicator: 508f5883ac79bdbc10254092de3f28f571d261cd
|
||||||
|
ed25519_edwards: 7353ba759ea9f4646cbf481c2ef949625c8ce4cf
|
||||||
|
hashlib: 983cdbd5ee2529b908876b57a7217c09c6bc148a
|
||||||
|
hashlib_codecs: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b
|
||||||
|
introduction_screen: 4a90e557630b28834479ed9c64a9d2d0185d8e48
|
||||||
|
libsignal_protocol_dart: 618f0c0b49534245a640a31d204265440cbac9ee
|
||||||
|
lottie: 4f1a5a52bdf1e1c1e12fa97c96174dcb05419e19
|
||||||
|
mutex: 84ca903a3ac863735e3228c75a212133621f680f
|
||||||
|
optional: 71c638891ce4f2aff35c7387727989f31f9d877d
|
||||||
|
photo_view: a13ca2fc387a3fb1276126959e092c44d0029987
|
||||||
|
pointycastle: bbd8569f68a7fccbdf0b92d0b44a9219c126c8dd
|
||||||
|
qr_dart: ff808bb3f354e6a7029ec953cbe0144a42021db6
|
||||||
|
x25519: ecb1d357714537bba6e276ef45f093846d4beaee
|
||||||
42
config.yaml
Normal file
42
config.yaml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
qr_dart:
|
||||||
|
git: https://github.com/kevmoo/qr.dart.git
|
||||||
|
|
||||||
|
mutex:
|
||||||
|
git: https://github.com/hoylen/dart-mutex.git
|
||||||
|
|
||||||
|
photo_view:
|
||||||
|
git: https://github.com/bluefireteam/photo_view.git
|
||||||
|
|
||||||
|
introduction_screen:
|
||||||
|
git: https://github.com/Pyozer/introduction_screen.git
|
||||||
|
|
||||||
|
dots_indicator:
|
||||||
|
git: https://github.com/Pyozer/dots_indicator.git
|
||||||
|
|
||||||
|
hashlib:
|
||||||
|
git: https://github.com/bitanon/hashlib.git
|
||||||
|
replace:
|
||||||
|
-
|
||||||
|
- "abstract class MACHashBase"
|
||||||
|
- "abstract mixin class MACHashBase"
|
||||||
|
dependencies:
|
||||||
|
hashlib_codecs:
|
||||||
|
git: https://github.com/bitanon/hashlib_codecs.git
|
||||||
|
|
||||||
|
lottie:
|
||||||
|
git: https://github.com/xvrh/lottie-flutter.git
|
||||||
|
|
||||||
|
|
||||||
|
libsignal_protocol_dart:
|
||||||
|
git: https://github.com/MixinNetwork/libsignal_protocol_dart.git
|
||||||
|
dependencies:
|
||||||
|
adaptive_number:
|
||||||
|
git: https://github.com/lemoony/adaptive_number_dart
|
||||||
|
ed25519_edwards:
|
||||||
|
git: https://github.com/Tougee/ed25519.git
|
||||||
|
optional:
|
||||||
|
git: https://github.com/tonio-ramirez/optional.dart.git
|
||||||
|
pointycastle:
|
||||||
|
git: https://github.com/bcgit/pc-dart.git
|
||||||
|
x25519:
|
||||||
|
git: https://github.com/Tougee/curve25519.git
|
||||||
21
dots_indicator/LICENSE
Normal file
21
dots_indicator/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Jean-Charles Moussé
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
4
dots_indicator/lib/dots_indicator.dart
Normal file
4
dots_indicator/lib/dots_indicator.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
library dots_indicator;
|
||||||
|
|
||||||
|
export 'src/dots_indicator.dart' show DotsIndicator;
|
||||||
|
export 'src/dots_decorator.dart' show DotsDecorator;
|
||||||
156
dots_indicator/lib/src/dots_decorator.dart
Normal file
156
dots_indicator/lib/src/dots_decorator.dart
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
const Size kDefaultSize = Size.square(9.0);
|
||||||
|
const Size kDefaultFadeOutSize = Size.square(6.0);
|
||||||
|
const EdgeInsets kDefaultSpacing = EdgeInsets.all(6.0);
|
||||||
|
const ShapeBorder kDefaultShape = CircleBorder();
|
||||||
|
|
||||||
|
class DotsDecorator {
|
||||||
|
/// Inactive dot color
|
||||||
|
///
|
||||||
|
/// @Default `Colors.grey`
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// List of inactive dot colors
|
||||||
|
/// One color by dot
|
||||||
|
///
|
||||||
|
/// @Default `Value of color parameter applied to each dot`
|
||||||
|
final List<Color> colors;
|
||||||
|
|
||||||
|
/// Active dot color
|
||||||
|
///
|
||||||
|
/// @Default `Theme.of(context).primaryColor`
|
||||||
|
final Color? activeColor;
|
||||||
|
|
||||||
|
/// List of active dot colors
|
||||||
|
/// One color by dot
|
||||||
|
///
|
||||||
|
/// @Default `Value of activeColor parameter applied to each dot`
|
||||||
|
final List<Color> activeColors;
|
||||||
|
|
||||||
|
/// Inactive dot size
|
||||||
|
///
|
||||||
|
/// @Default `Size.square(9.0)`
|
||||||
|
final Size size;
|
||||||
|
|
||||||
|
/// List of inactive dot size
|
||||||
|
/// One size by dot
|
||||||
|
///
|
||||||
|
/// @Default `Value of size parameter applied to each dot`
|
||||||
|
final List<Size> sizes;
|
||||||
|
|
||||||
|
/// Fade out dot size
|
||||||
|
///
|
||||||
|
/// @Default `Size.square(6.0)`
|
||||||
|
final Size fadeOutSize;
|
||||||
|
|
||||||
|
/// List of fade out dot size
|
||||||
|
/// One size by dot
|
||||||
|
///
|
||||||
|
/// @Default `Value of activeSize parameter applied to each dot`
|
||||||
|
final List<Size> fadeOutSizes;
|
||||||
|
|
||||||
|
/// Active dot size
|
||||||
|
///
|
||||||
|
/// @Default `Size.square(9.0)`
|
||||||
|
final Size activeSize;
|
||||||
|
|
||||||
|
/// List of active dot size
|
||||||
|
/// One size by dot
|
||||||
|
///
|
||||||
|
/// @Default `Value of activeSize parameter applied to each dot`
|
||||||
|
final List<Size> activeSizes;
|
||||||
|
|
||||||
|
/// Inactive dot shape
|
||||||
|
///
|
||||||
|
/// @Default `CircleBorder()`
|
||||||
|
final ShapeBorder shape;
|
||||||
|
|
||||||
|
/// List of inactive dot shape
|
||||||
|
/// One shape by dot
|
||||||
|
///
|
||||||
|
/// @Default `Value of shape parameter applied to each dot`
|
||||||
|
final List<ShapeBorder> shapes;
|
||||||
|
|
||||||
|
/// Active dot shape
|
||||||
|
///
|
||||||
|
/// @Default `CircleBorder()`
|
||||||
|
final ShapeBorder activeShape;
|
||||||
|
|
||||||
|
/// List of active dot shapes
|
||||||
|
/// One shape by dot
|
||||||
|
///
|
||||||
|
/// @Default `Value of activeShape parameter applied to each dot`
|
||||||
|
final List<ShapeBorder> activeShapes;
|
||||||
|
|
||||||
|
/// Spacing between dots
|
||||||
|
///
|
||||||
|
/// @Default `EdgeInsets.all(6.0)`
|
||||||
|
final EdgeInsets spacing;
|
||||||
|
|
||||||
|
/// Shadows of the dots
|
||||||
|
///
|
||||||
|
/// @Default `null`
|
||||||
|
final List<BoxShadow>? shadows;
|
||||||
|
|
||||||
|
/// Shadows of the active dots
|
||||||
|
///
|
||||||
|
/// @Default `shadows` or `null`
|
||||||
|
final List<BoxShadow>? activeShadows;
|
||||||
|
|
||||||
|
const DotsDecorator({
|
||||||
|
this.color = Colors.grey,
|
||||||
|
this.colors = const [],
|
||||||
|
this.activeColor,
|
||||||
|
this.activeColors = const [],
|
||||||
|
this.size = kDefaultSize,
|
||||||
|
this.sizes = const [],
|
||||||
|
this.fadeOutSize = kDefaultFadeOutSize,
|
||||||
|
this.fadeOutSizes = const [],
|
||||||
|
this.activeSize = kDefaultSize,
|
||||||
|
this.activeSizes = const [],
|
||||||
|
this.shape = kDefaultShape,
|
||||||
|
this.shapes = const [],
|
||||||
|
this.activeShape = kDefaultShape,
|
||||||
|
this.activeShapes = const [],
|
||||||
|
this.spacing = kDefaultSpacing,
|
||||||
|
this.shadows,
|
||||||
|
this.activeShadows,
|
||||||
|
});
|
||||||
|
|
||||||
|
Color? getActiveColor(int index) {
|
||||||
|
return activeColors.isNotEmpty ? activeColors[index] : activeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getColor(int index) {
|
||||||
|
return colors.isNotEmpty ? colors[index] : color;
|
||||||
|
}
|
||||||
|
|
||||||
|
Size getFadeOutSize(int index) {
|
||||||
|
return fadeOutSizes.isNotEmpty ? fadeOutSizes[index] : fadeOutSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Size getActiveSize(int index) {
|
||||||
|
return activeSizes.isNotEmpty ? activeSizes[index] : activeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Size getSize(int index) {
|
||||||
|
return sizes.isNotEmpty ? sizes[index] : size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapeBorder getActiveShape(int index) {
|
||||||
|
return activeShapes.isNotEmpty ? activeShapes[index] : activeShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapeBorder getShape(int index) {
|
||||||
|
return shapes.isNotEmpty ? shapes[index] : shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BoxShadow>? getShadows(int index) {
|
||||||
|
return shadows;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BoxShadow>? getActiveShadows(int index) {
|
||||||
|
return activeShadows ?? shadows;
|
||||||
|
}
|
||||||
|
}
|
||||||
187
dots_indicator/lib/src/dots_indicator.dart
Normal file
187
dots_indicator/lib/src/dots_indicator.dart
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
library dots_indicator;
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:dots_indicator/src/dots_decorator.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
typedef OnTap = void Function(int position);
|
||||||
|
|
||||||
|
class DotsIndicator extends StatelessWidget {
|
||||||
|
final int dotsCount;
|
||||||
|
final double position;
|
||||||
|
|
||||||
|
/// If true, the last dot will fade out when the position is at the last dot.
|
||||||
|
final bool fadeOutLastDot;
|
||||||
|
|
||||||
|
/// Distance from currently selected dot to the one that fades out.
|
||||||
|
final int fadeOutDistance;
|
||||||
|
final DotsDecorator decorator;
|
||||||
|
final Axis axis;
|
||||||
|
final bool reversed;
|
||||||
|
final OnTap? onTap;
|
||||||
|
final MainAxisSize mainAxisSize;
|
||||||
|
final MainAxisAlignment mainAxisAlignment;
|
||||||
|
|
||||||
|
/// If true, the dots will animate when the position changes.
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
|
/// Duration of the animation when the position changes.
|
||||||
|
final Duration animationDuration;
|
||||||
|
|
||||||
|
DotsIndicator({
|
||||||
|
super.key,
|
||||||
|
required this.dotsCount,
|
||||||
|
this.position = 0.0,
|
||||||
|
this.decorator = const DotsDecorator(),
|
||||||
|
this.axis = Axis.horizontal,
|
||||||
|
this.reversed = false,
|
||||||
|
this.mainAxisSize = MainAxisSize.min,
|
||||||
|
this.mainAxisAlignment = MainAxisAlignment.center,
|
||||||
|
this.onTap,
|
||||||
|
this.fadeOutLastDot = false,
|
||||||
|
this.fadeOutDistance = 0,
|
||||||
|
this.animate = false,
|
||||||
|
this.animationDuration = const Duration(milliseconds: 200),
|
||||||
|
}) : assert(dotsCount > 0, 'dotsCount must be superior to zero'),
|
||||||
|
assert(position >= 0.0, 'position must be superior or equals to zero'),
|
||||||
|
assert(
|
||||||
|
position < dotsCount,
|
||||||
|
"position must be less than dotsCount",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
decorator.colors.isEmpty || decorator.colors.length == dotsCount,
|
||||||
|
"colors param in decorator must empty or have same length as dotsCount parameter",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
decorator.activeColors.isEmpty ||
|
||||||
|
decorator.activeColors.length == dotsCount,
|
||||||
|
"activeColors param in decorator must empty or have same length as dotsCount parameter",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
decorator.sizes.isEmpty || decorator.sizes.length == dotsCount,
|
||||||
|
"sizes param in decorator must empty or have same length as dotsCount parameter",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
decorator.activeSizes.isEmpty ||
|
||||||
|
decorator.activeSizes.length == dotsCount,
|
||||||
|
"activeSizes param in decorator must empty or have same length as dotsCount parameter",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
decorator.shapes.isEmpty || decorator.shapes.length == dotsCount,
|
||||||
|
"shapes param in decorator must empty or have same length as dotsCount parameter",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
decorator.activeShapes.isEmpty ||
|
||||||
|
decorator.activeShapes.length == dotsCount,
|
||||||
|
"activeShapes param in decorator must empty or have same length as dotsCount parameter",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
fadeOutLastDot == false || fadeOutDistance > 0,
|
||||||
|
"fadeOutDistace must be superior to zero when fadeOutLastDot is true",
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
fadeOutDistance < dotsCount,
|
||||||
|
"fadeOutDistace must be inferior to dotsCount",
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _wrapInkwell(Widget dot, int index) {
|
||||||
|
return InkWell(
|
||||||
|
customBorder: position == index
|
||||||
|
? decorator.getActiveShape(index)
|
||||||
|
: decorator.getShape(index),
|
||||||
|
onTap: () => onTap!(index),
|
||||||
|
child: dot,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDot(BuildContext context, int index) {
|
||||||
|
final double absPositionIndexRelation = (position - index).abs();
|
||||||
|
final bool isCurrentlyVisible = absPositionIndexRelation <= fadeOutDistance;
|
||||||
|
|
||||||
|
final double lerpValue = min(1.0, absPositionIndexRelation).toDouble();
|
||||||
|
|
||||||
|
Size size = Size.lerp(
|
||||||
|
decorator.getActiveSize(index),
|
||||||
|
decorator.getSize(index),
|
||||||
|
lerpValue,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
if (fadeOutLastDot && absPositionIndexRelation >= fadeOutDistance) {
|
||||||
|
size = Size.lerp(
|
||||||
|
decorator.getSize(index),
|
||||||
|
decorator.getFadeOutSize(index),
|
||||||
|
absPositionIndexRelation == fadeOutDistance ? 1 : 0.0,
|
||||||
|
)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final dot = Container(
|
||||||
|
height: fadeOutLastDot && isCurrentlyVisible
|
||||||
|
? max(
|
||||||
|
max(decorator.getActiveSize(index).height,
|
||||||
|
decorator.getSize(index).height),
|
||||||
|
decorator.getFadeOutSize(index).height,
|
||||||
|
) +
|
||||||
|
(axis == Axis.horizontal
|
||||||
|
? decorator.spacing.vertical
|
||||||
|
: decorator.spacing.horizontal)
|
||||||
|
: null,
|
||||||
|
child: Center(
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
duration: animate ? animationDuration : Duration.zero,
|
||||||
|
opacity:
|
||||||
|
!fadeOutLastDot || absPositionIndexRelation <= fadeOutDistance
|
||||||
|
? 1.0
|
||||||
|
: 0.0,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: animate ? animationDuration : Duration.zero,
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
margin: fadeOutLastDot && !isCurrentlyVisible
|
||||||
|
? EdgeInsets.all(0)
|
||||||
|
: decorator.spacing,
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
color: Color.lerp(
|
||||||
|
decorator.getActiveColor(index) ?? Theme.of(context).primaryColor,
|
||||||
|
decorator.getColor(index),
|
||||||
|
lerpValue,
|
||||||
|
),
|
||||||
|
shape: ShapeBorder.lerp(
|
||||||
|
decorator.getActiveShape(index),
|
||||||
|
decorator.getShape(index),
|
||||||
|
lerpValue,
|
||||||
|
)!,
|
||||||
|
shadows: BoxShadow.lerpList(
|
||||||
|
decorator.getActiveShadows(index),
|
||||||
|
decorator.getShadows(index),
|
||||||
|
lerpValue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return onTap == null ? dot : _wrapInkwell(dot, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final dotsList = List<Widget>.generate(
|
||||||
|
dotsCount,
|
||||||
|
(i) => _buildDot(context, i),
|
||||||
|
);
|
||||||
|
final dots = reversed ? dotsList.reversed.toList() : dotsList;
|
||||||
|
|
||||||
|
return axis == Axis.vertical
|
||||||
|
? Column(
|
||||||
|
mainAxisAlignment: mainAxisAlignment,
|
||||||
|
mainAxisSize: mainAxisSize,
|
||||||
|
children: dots,
|
||||||
|
)
|
||||||
|
: Row(
|
||||||
|
mainAxisAlignment: mainAxisAlignment,
|
||||||
|
mainAxisSize: mainAxisSize,
|
||||||
|
children: dots,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
dots_indicator/pubspec.yaml
Normal file
14
dots_indicator/pubspec.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
name: dots_indicator
|
||||||
|
description: Dots indicator to show progression of a PageView for example
|
||||||
|
version: 4.0.1
|
||||||
|
homepage: https://github.com/pyozer/dots_indicator
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.17.0 <4.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_lints: ^5.0.0
|
||||||
201
ed25519_edwards/LICENSE
Normal file
201
ed25519_edwards/LICENSE
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
204
ed25519_edwards/lib/ed25519_edwards.dart
Normal file
204
ed25519_edwards/lib/ed25519_edwards.dart
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
/// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||||
|
/// https://ed25519.cr.yp.to/.
|
||||||
|
///
|
||||||
|
/// These functions are also compatible with the “Ed25519” function defined in
|
||||||
|
/// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||||
|
/// representation includes a public key suffix to make multiple signing
|
||||||
|
/// operations with the same key more efficient. This package refers to the RFC
|
||||||
|
/// 8032 private key as the “seed”.
|
||||||
|
|
||||||
|
library edwards25519;
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:ed25519_edwards/src/edwards25519.dart';
|
||||||
|
import 'package:ed25519_edwards/src/util.dart';
|
||||||
|
|
||||||
|
/// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||||
|
const PublicKeySize = 32;
|
||||||
|
|
||||||
|
/// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||||
|
const PrivateKeySize = 64;
|
||||||
|
|
||||||
|
/// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||||
|
const SignatureSize = 64;
|
||||||
|
|
||||||
|
/// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||||
|
const SeedSize = 32;
|
||||||
|
|
||||||
|
/// PublicKey is the type of Ed25519 public keys.
|
||||||
|
class PublicKey {
|
||||||
|
List<int> bytes;
|
||||||
|
|
||||||
|
PublicKey(this.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PrivateKey is the type of Ed25519 private keys.
|
||||||
|
class PrivateKey {
|
||||||
|
List<int> bytes;
|
||||||
|
|
||||||
|
PrivateKey(this.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// KeyPair is the type of Ed25519 public/private key pair.
|
||||||
|
class KeyPair {
|
||||||
|
final PrivateKey privateKey;
|
||||||
|
|
||||||
|
final PublicKey publicKey;
|
||||||
|
|
||||||
|
KeyPair(this.privateKey, this.publicKey);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => publicKey.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is KeyPair &&
|
||||||
|
publicKey == other.publicKey &&
|
||||||
|
privateKey == other.privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// public returns the PublicKey corresponding to PrivateKey.
|
||||||
|
PublicKey public(PrivateKey privateKey) {
|
||||||
|
var publicKey = privateKey.bytes.sublist(32, 32 + PublicKeySize);
|
||||||
|
return PublicKey(publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Seed returns the private key seed corresponding to priv. It is provided for
|
||||||
|
/// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
|
||||||
|
/// in this package.
|
||||||
|
Uint8List seed(PrivateKey privateKey) {
|
||||||
|
var seed = privateKey.bytes.sublist(0, SeedSize);
|
||||||
|
return seed as Uint8List;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GenerateKey generates a public/private key pair using entropy from secure random.
|
||||||
|
KeyPair generateKey() {
|
||||||
|
var seed = Uint8List(32);
|
||||||
|
fillBytesWithSecureRandomNumbers(seed);
|
||||||
|
var privateKey = newKeyFromSeed(seed);
|
||||||
|
var publicKey = privateKey.bytes.sublist(32, PrivateKeySize);
|
||||||
|
return KeyPair(privateKey, PublicKey(publicKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NewKeyFromSeed calculates a private key from a seed. It will throw
|
||||||
|
/// ArgumentError if seed.length is not SeedSize.
|
||||||
|
/// This function is provided for interoperability with RFC 8032.
|
||||||
|
/// RFC 8032's private keys correspond to seeds in this package.
|
||||||
|
PrivateKey newKeyFromSeed(Uint8List seed) {
|
||||||
|
if (seed.length != SeedSize) {
|
||||||
|
throw ArgumentError('ed25519: bad seed length ${seed.length}');
|
||||||
|
}
|
||||||
|
var h = sha512.convert(seed);
|
||||||
|
var digest = h.bytes.sublist(0, 32);
|
||||||
|
digest[0] &= 248;
|
||||||
|
digest[31] &= 127;
|
||||||
|
digest[31] |= 64;
|
||||||
|
|
||||||
|
var A = ExtendedGroupElement();
|
||||||
|
var hBytes = digest.sublist(0);
|
||||||
|
GeScalarMultBase(A, hBytes as Uint8List);
|
||||||
|
var publicKeyBytes = Uint8List(32);
|
||||||
|
A.ToBytes(publicKeyBytes);
|
||||||
|
|
||||||
|
var privateKey = Uint8List(PrivateKeySize);
|
||||||
|
arrayCopy(seed, 0, privateKey, 0, 32);
|
||||||
|
arrayCopy(publicKeyBytes, 0, privateKey, 32, 32);
|
||||||
|
return PrivateKey(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign signs the message with privateKey and returns a signature. It will
|
||||||
|
/// throw ArumentError if privateKey.bytes.length is not PrivateKeySize.
|
||||||
|
Uint8List sign(PrivateKey privateKey, Uint8List message) {
|
||||||
|
if (privateKey.bytes.length != PrivateKeySize) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'ed25519: bad privateKey length ${privateKey.bytes.length}');
|
||||||
|
}
|
||||||
|
var h = sha512.convert(privateKey.bytes.sublist(0, 32));
|
||||||
|
var digest1 = h.bytes;
|
||||||
|
var expandedSecretKey = digest1.sublist(0, 32);
|
||||||
|
expandedSecretKey[0] &= 248;
|
||||||
|
expandedSecretKey[31] &= 63;
|
||||||
|
expandedSecretKey[31] |= 64;
|
||||||
|
|
||||||
|
var output = AccumulatorSink<Digest>();
|
||||||
|
var input = sha512.startChunkedConversion(output);
|
||||||
|
input.add(digest1.sublist(32));
|
||||||
|
input.add(message);
|
||||||
|
input.close();
|
||||||
|
var messageDigest = output.events.single.bytes;
|
||||||
|
|
||||||
|
var messageDigestReduced = Uint8List(32);
|
||||||
|
ScReduce(messageDigestReduced, messageDigest as Uint8List);
|
||||||
|
var R = ExtendedGroupElement();
|
||||||
|
GeScalarMultBase(R, messageDigestReduced);
|
||||||
|
|
||||||
|
var encodedR = Uint8List(32);
|
||||||
|
R.ToBytes(encodedR);
|
||||||
|
|
||||||
|
output = AccumulatorSink<Digest>();
|
||||||
|
input = sha512.startChunkedConversion(output);
|
||||||
|
input.add(encodedR);
|
||||||
|
input.add(privateKey.bytes.sublist(32));
|
||||||
|
input.add(message);
|
||||||
|
input.close();
|
||||||
|
var hramDigest = output.events.single.bytes;
|
||||||
|
var hramDigestReduced = Uint8List(32);
|
||||||
|
ScReduce(hramDigestReduced, hramDigest as Uint8List);
|
||||||
|
|
||||||
|
var s = Uint8List(32);
|
||||||
|
ScMulAdd(s, hramDigestReduced, expandedSecretKey as Uint8List,
|
||||||
|
messageDigestReduced);
|
||||||
|
|
||||||
|
var signature = Uint8List(SignatureSize);
|
||||||
|
arrayCopy(encodedR, 0, signature, 0, 32);
|
||||||
|
arrayCopy(s, 0, signature, 32, 32);
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||||
|
/// will throw ArgumentError if publicKey.bytes.length is not PublicKeySize.
|
||||||
|
bool verify(PublicKey publicKey, Uint8List message, Uint8List sig) {
|
||||||
|
if (publicKey.bytes.length != PublicKeySize) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'ed25519: bad publicKey length ${publicKey.bytes.length}');
|
||||||
|
}
|
||||||
|
if (sig.length != SignatureSize || sig[63] & 224 != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var A = ExtendedGroupElement();
|
||||||
|
var publicKeyBytes = Uint8List.fromList(publicKey.bytes);
|
||||||
|
if (!A.FromBytes(publicKeyBytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FeNeg(A.X, A.X);
|
||||||
|
FeNeg(A.T, A.T);
|
||||||
|
|
||||||
|
var output = AccumulatorSink<Digest>();
|
||||||
|
var input = sha512.startChunkedConversion(output);
|
||||||
|
input.add(sig.sublist(0, 32));
|
||||||
|
input.add(publicKeyBytes);
|
||||||
|
input.add(message);
|
||||||
|
input.close();
|
||||||
|
var digest = output.events.single.bytes;
|
||||||
|
|
||||||
|
var hReduced = Uint8List(32);
|
||||||
|
ScReduce(hReduced, digest as Uint8List);
|
||||||
|
|
||||||
|
var R = ProjectiveGroupElement();
|
||||||
|
var s = sig.sublist(32);
|
||||||
|
|
||||||
|
if (!ScMinimal(s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GeDoubleScalarMultVartime(R, hReduced, A, s);
|
||||||
|
|
||||||
|
var checkR = Uint8List(32);
|
||||||
|
R.ToBytes(checkR);
|
||||||
|
return ListEquality().equals(sig.sublist(0, 32), checkR);
|
||||||
|
}
|
||||||
10162
ed25519_edwards/lib/src/const.dart
Normal file
10162
ed25519_edwards/lib/src/const.dart
Normal file
File diff suppressed because it is too large
Load diff
2127
ed25519_edwards/lib/src/edwards25519.dart
Normal file
2127
ed25519_edwards/lib/src/edwards25519.dart
Normal file
File diff suppressed because it is too large
Load diff
18
ed25519_edwards/lib/src/numbers.dart
Normal file
18
ed25519_edwards/lib/src/numbers.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:adaptive_number/adaptive_number.dart';
|
||||||
|
|
||||||
|
abstract class Numbers {
|
||||||
|
static Number v8 = Number(8);
|
||||||
|
static Number v15 = Number(15);
|
||||||
|
static Number v19 = Number(19);
|
||||||
|
static Number v24 = Number(24);
|
||||||
|
static Number v25 = Number(25);
|
||||||
|
static Number v26 = Number(26);
|
||||||
|
static Number v38 = Number(38);
|
||||||
|
static Number v136657 = Number(136657);
|
||||||
|
static Number v2097151 = Number(2097151);
|
||||||
|
static Number v470296 = Number(470296);
|
||||||
|
static Number v683901 = Number(683901);
|
||||||
|
static Number v654183 = Number(654183);
|
||||||
|
static Number v666643 = Number(666643);
|
||||||
|
static Number v997805 = Number(997805);
|
||||||
|
}
|
||||||
16
ed25519_edwards/lib/src/util.dart
Normal file
16
ed25519_edwards/lib/src/util.dart
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
void arrayCopy(List src, int srcPos, List dest, int destPos, int length) {
|
||||||
|
dest.setRange(destPos, length + destPos, src, srcPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
final _defaultSecureRandom = Random.secure();
|
||||||
|
|
||||||
|
void fillBytesWithSecureRandomNumbers(Uint8List bytes, {Random? random}) {
|
||||||
|
random ??= _defaultSecureRandom;
|
||||||
|
for (var i = 0; i < bytes.length; i++) {
|
||||||
|
bytes[i] = random.nextInt(256);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
ed25519_edwards/pubspec.yaml
Normal file
18
ed25519_edwards/pubspec.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
name: ed25519_edwards
|
||||||
|
description: Dart port of ed25519 from Go Cryptography ed25519
|
||||||
|
version: 0.3.1
|
||||||
|
homepage: https://github.com/Tougee/ed25519
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
collection: ^1.15.0
|
||||||
|
crypto: ^3.0.0
|
||||||
|
convert: ^3.0.0
|
||||||
|
adaptive_number: ^1.0.0
|
||||||
|
dev_dependencies:
|
||||||
|
pedantic: ^1.10.0
|
||||||
|
test: ^1.16.4
|
||||||
|
hex: ^0.2.0
|
||||||
|
benchmark_harness: ^2.0.0
|
||||||
38
ed25519_edwards/test/benchmark/ed25519_benchmark.dart
Normal file
38
ed25519_edwards/test/benchmark/ed25519_benchmark.dart
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:ed25519_edwards/ed25519_edwards.dart';
|
||||||
|
|
||||||
|
import 'rate_benchmark.dart';
|
||||||
|
|
||||||
|
class Ed25519Benchmark extends RateBenchmark {
|
||||||
|
Ed25519Benchmark(bool forSigning, [int dataLength = 1024 * 1024])
|
||||||
|
: _forSigning = forSigning,
|
||||||
|
_data = Uint8List(dataLength),
|
||||||
|
super('Ed25519 - ${forSigning ? 'sign' : 'verify'}');
|
||||||
|
|
||||||
|
final Uint8List _data;
|
||||||
|
final bool _forSigning;
|
||||||
|
late final KeyPair _keyPair;
|
||||||
|
Uint8List? _signature;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setup() {
|
||||||
|
_keyPair = generateKey();
|
||||||
|
_signature = sign(_keyPair.privateKey, _data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run() {
|
||||||
|
if (_forSigning) {
|
||||||
|
sign(_keyPair.privateKey, _data);
|
||||||
|
} else if (_signature != null) {
|
||||||
|
verify(_keyPair.publicKey, _data, _signature!);
|
||||||
|
}
|
||||||
|
addSample(_data.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Ed25519Benchmark(true).report();
|
||||||
|
Ed25519Benchmark(false).report();
|
||||||
|
}
|
||||||
64
ed25519_edwards/test/benchmark/rate_benchmark.dart
Normal file
64
ed25519_edwards/test/benchmark/rate_benchmark.dart
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||||
|
|
||||||
|
abstract class RateBenchmark extends BenchmarkBase {
|
||||||
|
RateBenchmark(String name, {this.runLength = 5000})
|
||||||
|
: super(name, emitter: RateEmitter()) {
|
||||||
|
(emitter as RateEmitter).benchmark = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int runLength;
|
||||||
|
int _totalData = 0;
|
||||||
|
int _iterations = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ScoreEmitter get emitter => super.emitter;
|
||||||
|
|
||||||
|
void addSample(int processedData) {
|
||||||
|
_totalData += processedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void exercise() {
|
||||||
|
_totalData = 0;
|
||||||
|
_iterations = 0;
|
||||||
|
|
||||||
|
var watch = Stopwatch()..start();
|
||||||
|
while (watch.elapsedMilliseconds < runLength) {
|
||||||
|
run();
|
||||||
|
_iterations++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RateEmitter implements ScoreEmitter {
|
||||||
|
late RateBenchmark benchmark;
|
||||||
|
|
||||||
|
int get totalData => benchmark._totalData;
|
||||||
|
int get iterations => benchmark._iterations;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void emit(String testName, double value) {
|
||||||
|
final ms = value / 1000;
|
||||||
|
final s = ms / 1000;
|
||||||
|
final date = DateTime.now().toString().split('.')[0];
|
||||||
|
|
||||||
|
print('| $date | '
|
||||||
|
'$testName | '
|
||||||
|
'${_formatDataLength(totalData / s)}/s | '
|
||||||
|
'$iterations iterations | '
|
||||||
|
'${ms.toInt()} ms | '
|
||||||
|
'${_formatDataLength(totalData)} |');
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDataLength(num dataLen) {
|
||||||
|
if (dataLen < 1024) {
|
||||||
|
return '${dataLen.toStringAsFixed(2)} B';
|
||||||
|
} else if (dataLen < (1024 * 1024)) {
|
||||||
|
return '${(dataLen / 1024).toStringAsFixed(2)} KB';
|
||||||
|
} else if (dataLen < (1024 * 1024 * 1024)) {
|
||||||
|
return '${(dataLen / (1024 * 1024)).toStringAsFixed(2)} MB';
|
||||||
|
} else {
|
||||||
|
return '${(dataLen / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
ed25519_edwards/test/ed25519_test.dart
Normal file
67
ed25519_edwards/test/ed25519_test.dart
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:ed25519_edwards/ed25519_edwards.dart' as ed;
|
||||||
|
import 'package:hex/hex.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
void testVector(
|
||||||
|
String plain, String seedStr, String publicStr, String sigStr) {
|
||||||
|
var seed = HEX.decode(seedStr);
|
||||||
|
var public = HEX.decode(publicStr);
|
||||||
|
var privateKey = ed.PrivateKey([...seed, ...public]);
|
||||||
|
var publicKey = ed.public(privateKey);
|
||||||
|
var message = HEX.decode(plain);
|
||||||
|
var sig = ed.sign(privateKey, message as Uint8List);
|
||||||
|
|
||||||
|
var targetSig = HEX.decode(sigStr);
|
||||||
|
assert(ListEquality().equals(targetSig, sig));
|
||||||
|
|
||||||
|
var result = ed.verify(publicKey, message, sig);
|
||||||
|
assert(result == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('test rfc8032 cases', () {
|
||||||
|
testVector(
|
||||||
|
'',
|
||||||
|
'9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60',
|
||||||
|
'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a',
|
||||||
|
'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b');
|
||||||
|
testVector(
|
||||||
|
'72',
|
||||||
|
'4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb',
|
||||||
|
'3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c',
|
||||||
|
'92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00');
|
||||||
|
testVector(
|
||||||
|
'af82',
|
||||||
|
'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7',
|
||||||
|
'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025',
|
||||||
|
'6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a');
|
||||||
|
testVector(
|
||||||
|
'08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0',
|
||||||
|
'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5',
|
||||||
|
'278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e',
|
||||||
|
'0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03');
|
||||||
|
testVector(
|
||||||
|
'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f',
|
||||||
|
'833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
|
||||||
|
'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
|
||||||
|
'dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('testSignVerify', () {
|
||||||
|
var keyPair = ed.generateKey();
|
||||||
|
var privateKey = keyPair.privateKey;
|
||||||
|
var publicKey = keyPair.publicKey;
|
||||||
|
var message = utf8.encode('test message');
|
||||||
|
var sig = ed.sign(privateKey, message as Uint8List);
|
||||||
|
var result = ed.verify(publicKey, message, sig);
|
||||||
|
assert(result == true);
|
||||||
|
|
||||||
|
var wrongMessage = utf8.encode('wrong message');
|
||||||
|
var wrongResult = ed.verify(publicKey, wrongMessage as Uint8List, sig);
|
||||||
|
assert(wrongResult == false);
|
||||||
|
});
|
||||||
|
}
|
||||||
29
hashlib/LICENSE
Normal file
29
hashlib/LICENSE
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023, Sudipto Chandra
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
7
hashlib/lib/codecs.dart
Normal file
7
hashlib/lib/codecs.dart
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
/// Implementations of fast and error resilient codecs
|
||||||
|
library;
|
||||||
|
|
||||||
|
export 'package:hashlib_codecs/hashlib_codecs.dart';
|
||||||
11
hashlib/lib/hashlib.dart
Normal file
11
hashlib/lib/hashlib.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
/// Collection of hashing algorithms, checksum generators, message
|
||||||
|
/// authentication code (MAC) utilities and key derivation functions.
|
||||||
|
///
|
||||||
|
/// This library serves as a convenience export so that all supported
|
||||||
|
/// algorithms can be used with a single import.
|
||||||
|
library;
|
||||||
|
|
||||||
|
export 'src/hashlib.dart';
|
||||||
8
hashlib/lib/random.dart
Normal file
8
hashlib/lib/random.dart
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
/// Implementation of secure random generators based on hashlib
|
||||||
|
library;
|
||||||
|
|
||||||
|
export 'package:hashlib/src/random.dart';
|
||||||
|
export 'package:hashlib/src/uuid.dart';
|
||||||
41
hashlib/lib/src/adler32.dart
Normal file
41
hashlib/lib/src/adler32.dart
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/algorithms/adler32.dart';
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
/// [Adler-32][wiki] is composed of two sums accumulated per byte.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// According to [RFC-1950][rfc], the algorithm is described as follows:
|
||||||
|
/// - `a` is the sum of all bytes, `b` is the sum of all `a` values.
|
||||||
|
/// Both sums are done modulo `65521`.
|
||||||
|
/// - `a` is initialized to 1, `b` to 0.
|
||||||
|
/// - Final output is `b * 65536 + a`
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc1950.html
|
||||||
|
/// [wiki]: https://en.wikipedia.org/wiki/Adler-32
|
||||||
|
///
|
||||||
|
/// **WARNING: It should not be used for cryptographic purposes.**
|
||||||
|
const HashBase adler32 = _Adler32();
|
||||||
|
|
||||||
|
class _Adler32 extends HashBase {
|
||||||
|
const _Adler32();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name = 'ALDER-32';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Adler32Hash createSink() => Adler32Hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the Adler-32 value of a String
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [input] is the string to hash
|
||||||
|
/// - The [encoding] is the encoding to use. Default is `input.codeUnits`
|
||||||
|
int adler32code(String input, [Encoding? encoding]) {
|
||||||
|
return adler32.string(input, encoding).number();
|
||||||
|
}
|
||||||
46
hashlib/lib/src/algorithms/adler32.dart
Normal file
46
hashlib/lib/src/algorithms/adler32.dart
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
const int _adler32Mod = 65521;
|
||||||
|
|
||||||
|
/// This implementation is derived from the [ ZLIB Compressed Data Format
|
||||||
|
/// Specification version 3.3 ][rfc]
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc1950.html
|
||||||
|
class Adler32Hash extends HashDigestSink {
|
||||||
|
int a = 1, b = 0;
|
||||||
|
|
||||||
|
Adler32Hash();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 4;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
a = 1;
|
||||||
|
b = 0;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++) {
|
||||||
|
a = (a + chunk[start]) % _adler32Mod;
|
||||||
|
b = (b + a) % _adler32Mod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
return Uint8List.fromList([
|
||||||
|
b >>> 8,
|
||||||
|
b,
|
||||||
|
a >>> 8,
|
||||||
|
a,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
149
hashlib/lib/src/algorithms/argon2/argon2.dart
Normal file
149
hashlib/lib/src/algorithms/argon2/argon2.dart
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
import 'package:hashlib/src/core/kdf_base.dart';
|
||||||
|
|
||||||
|
import 'argon2_64bit.dart' if (dart.library.js) 'argon2_32bit.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
import 'security.dart';
|
||||||
|
|
||||||
|
export 'common.dart';
|
||||||
|
export 'security.dart';
|
||||||
|
|
||||||
|
/// Creates a context for [Argon2][wiki] password hashing.
|
||||||
|
///
|
||||||
|
/// Argon2 is a key derivation algorithm that was selected as the winner of the
|
||||||
|
/// 2015 [Password Hashing Contest][phc], and the best password hashing / key
|
||||||
|
/// derivation algorithm known to date.
|
||||||
|
///
|
||||||
|
/// Example of password hashing using Argon2:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// final salt = utf.encode("some salt")
|
||||||
|
/// final password = utf8.encode('password');
|
||||||
|
/// final argon2 = Argon2(
|
||||||
|
/// version: Argon2Version.v13,
|
||||||
|
/// type: Argon2Type.argon2id,
|
||||||
|
/// hashLength: 32,
|
||||||
|
/// iterations: 2,
|
||||||
|
/// parallelism: 8,
|
||||||
|
/// memorySizeKB: 1 << 18,
|
||||||
|
/// salt: salt,
|
||||||
|
/// );
|
||||||
|
/// final digest = argon2.encode(password);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [phc]: https://www.password-hashing.net/
|
||||||
|
/// [wiki]: https://en.wikipedia.org/wiki/Argon2
|
||||||
|
class Argon2 extends KeyDerivatorBase {
|
||||||
|
final Argon2Context _ctx;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => _ctx.type.name;
|
||||||
|
|
||||||
|
/// Argon2 Hash Type
|
||||||
|
Argon2Type get type => _ctx.type;
|
||||||
|
|
||||||
|
/// The current version is 0x13 (decimal: 19)
|
||||||
|
Argon2Version get version => _ctx.version;
|
||||||
|
|
||||||
|
/// Degree of parallelism (i.e. number of threads)
|
||||||
|
int get parallelism => _ctx.lanes;
|
||||||
|
|
||||||
|
/// Desired number of returned bytes
|
||||||
|
int get hashLength => _ctx.hashLength;
|
||||||
|
|
||||||
|
/// Amount of memory (in kibibytes) to use
|
||||||
|
int get memorySizeKB => _ctx.memorySizeKB;
|
||||||
|
|
||||||
|
/// Number of iterations to perform
|
||||||
|
int get iterations => _ctx.passes;
|
||||||
|
|
||||||
|
/// Salt (16 bytes recommended for password hashing)
|
||||||
|
List<int> get salt => _ctx.salt;
|
||||||
|
|
||||||
|
/// Optional key
|
||||||
|
List<int>? get key => _ctx.key;
|
||||||
|
|
||||||
|
/// Optional arbitrary additional data
|
||||||
|
List<int>? get personalization => _ctx.personalization;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get derivedKeyLength => hashLength;
|
||||||
|
|
||||||
|
/// Generate a derived key from a [password] using Argon2 algorithm
|
||||||
|
@override
|
||||||
|
Argon2HashDigest convert(List<int> password) {
|
||||||
|
final result = Argon2Internal(_ctx).convert(password);
|
||||||
|
return Argon2HashDigest(_ctx, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate an Argon2 encoded string from a [password]
|
||||||
|
String encode(List<int> password) => convert(password).encoded();
|
||||||
|
|
||||||
|
const Argon2._(this._ctx);
|
||||||
|
|
||||||
|
factory Argon2({
|
||||||
|
Argon2Type type = Argon2Type.argon2id,
|
||||||
|
Argon2Version version = Argon2Version.v13,
|
||||||
|
required int parallelism,
|
||||||
|
required int memorySizeKB,
|
||||||
|
required int iterations,
|
||||||
|
int? hashLength,
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? key,
|
||||||
|
List<int>? personalization,
|
||||||
|
}) {
|
||||||
|
var ctx = Argon2Context(
|
||||||
|
salt: salt,
|
||||||
|
version: version,
|
||||||
|
type: type,
|
||||||
|
hashLength: hashLength,
|
||||||
|
iterations: iterations,
|
||||||
|
parallelism: parallelism,
|
||||||
|
memorySizeKB: memorySizeKB,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
);
|
||||||
|
return Argon2._(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [Argon2] instance from [Argon2Security] parameter.
|
||||||
|
factory Argon2.fromSecurity(
|
||||||
|
Argon2Security security, {
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? key,
|
||||||
|
int? hashLength,
|
||||||
|
List<int>? personalization,
|
||||||
|
}) {
|
||||||
|
return Argon2(
|
||||||
|
salt: salt,
|
||||||
|
version: security.version,
|
||||||
|
type: security.type,
|
||||||
|
hashLength: hashLength,
|
||||||
|
iterations: security.t,
|
||||||
|
parallelism: security.p,
|
||||||
|
memorySizeKB: security.m,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [Argon2] instance from an encoded PHC-compliant string.
|
||||||
|
///
|
||||||
|
/// The encoded string may look like this:
|
||||||
|
/// `$argon2i$v=19$m=16,t=2,p=1$c29tZSBzYWx0$u1eU6mZFG4/OOoTdAtM5SQ`
|
||||||
|
factory Argon2.fromEncoded(
|
||||||
|
CryptData data, {
|
||||||
|
List<int>? key,
|
||||||
|
List<int>? personalization,
|
||||||
|
}) {
|
||||||
|
var ctx = Argon2Context.fromEncoded(
|
||||||
|
data,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
);
|
||||||
|
return Argon2._(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
516
hashlib/lib/src/algorithms/argon2/argon2_32bit.dart
Normal file
516
hashlib/lib/src/algorithms/argon2/argon2_32bit.dart
Normal file
|
|
@ -0,0 +1,516 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'common.dart';
|
||||||
|
import '../blake2/blake2b_32bit.dart';
|
||||||
|
|
||||||
|
const int _mask16 = 0xFFFF;
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const int _zero = 0;
|
||||||
|
const int _input = _zero + 256;
|
||||||
|
const int _address = _input + 256;
|
||||||
|
|
||||||
|
// slice 0 slice 1 slice 2 slice 3
|
||||||
|
// ____/\____ ____/\____ ____/\____ ____/\____
|
||||||
|
// / \ / \ / \ / \
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | segment 0 | segment 1 | segment 2 | segment 3 | -> lane 0
|
||||||
|
// +------------+------------+------------+-----------+
|
||||||
|
// | segment 4 | segment 5 | segment 6 | segment 7 | -> lane 1
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | segment 8 | segment 9 | segment 10 | segment 11 | -> lane 2
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | ... ... ... | ...
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | | | | | -> lane p - 1
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
|
||||||
|
class Argon2Internal {
|
||||||
|
final Argon2Context ctx;
|
||||||
|
final _blockR = Uint32List(256);
|
||||||
|
final _blockT = Uint32List(256);
|
||||||
|
final _temp = Uint32List(_address + 256);
|
||||||
|
|
||||||
|
Argon2Internal(this.ctx);
|
||||||
|
|
||||||
|
Uint8List convert(List<int> password) {
|
||||||
|
int i, j, k, p;
|
||||||
|
int pass, slice, lane;
|
||||||
|
var hash0 = Uint8List(64 + 8);
|
||||||
|
var hash0as32 = Uint32List.view(hash0.buffer);
|
||||||
|
var buffer32 = Uint32List(ctx.blocks << 8);
|
||||||
|
var buffer = Uint8List.view(buffer32.buffer);
|
||||||
|
var result = Uint8List(ctx.hashLength);
|
||||||
|
|
||||||
|
// H_0 Generation (64 + 8 = 72 bytes)
|
||||||
|
_initialHash(hash0, password);
|
||||||
|
|
||||||
|
// Initial block generation
|
||||||
|
// Lane Starting Blocks
|
||||||
|
k = 0;
|
||||||
|
hash0as32[16] = 0;
|
||||||
|
for (i = 0; i < ctx.lanes; i++, k += ctx.columns) {
|
||||||
|
// B[i][0] = H'^(1024)(H_0 || LE32(0) || LE32(i))
|
||||||
|
hash0as32[17] = i;
|
||||||
|
_expandHash(1024, hash0, buffer, k << 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second Lane Blocks
|
||||||
|
k = 1;
|
||||||
|
hash0as32[16] = 1;
|
||||||
|
for (i = 0; i < ctx.lanes; i++, k += ctx.columns) {
|
||||||
|
// B[i][1] = H'^(1024)(H_0 || LE32(1) || LE32(i))
|
||||||
|
hash0as32[17] = i;
|
||||||
|
_expandHash(1024, hash0, buffer, k << 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Further block generation
|
||||||
|
for (pass = 0; pass < ctx.passes; ++pass) {
|
||||||
|
for (slice = 0; slice < ctx.slices; ++slice) {
|
||||||
|
for (lane = 0; lane < ctx.lanes; ++lane) {
|
||||||
|
_fillSegment(buffer32, pass, slice, lane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalization
|
||||||
|
/* XOR the blocks */
|
||||||
|
j = ctx.columns - 1;
|
||||||
|
var block = Uint8List.view(buffer.buffer, j << 10, 1024);
|
||||||
|
for (k = 1; k < ctx.lanes; ++k) {
|
||||||
|
j += ctx.columns;
|
||||||
|
p = j << 10;
|
||||||
|
for (i = 0; i < 1024; ++i, ++p) {
|
||||||
|
block[i] ^= buffer[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash the result */
|
||||||
|
_expandHash(ctx.hashLength, block, result, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initialHash(Uint8List hash0, List<int> password) {
|
||||||
|
// H_0 = H^(64)(LE32(p) || LE32(T) || LE32(m) || LE32(t) ||
|
||||||
|
// LE32(v) || LE32(y) || LE32(length(P)) || P ||
|
||||||
|
// LE32(length(S)) || S || LE32(length(K)) || K ||
|
||||||
|
// LE32(length(X)) || X)
|
||||||
|
var blake2b = Blake2bHash(64);
|
||||||
|
blake2b.addUint32(ctx.lanes);
|
||||||
|
blake2b.addUint32(ctx.hashLength);
|
||||||
|
blake2b.addUint32(ctx.memorySizeKB);
|
||||||
|
blake2b.addUint32(ctx.passes);
|
||||||
|
blake2b.addUint32(ctx.version.value);
|
||||||
|
blake2b.addUint32(ctx.type.index);
|
||||||
|
blake2b.addUint32(password.length);
|
||||||
|
blake2b.add(password);
|
||||||
|
blake2b.addUint32(ctx.salt.length);
|
||||||
|
blake2b.add(ctx.salt);
|
||||||
|
blake2b.addUint32(ctx.key?.length ?? 0);
|
||||||
|
if (ctx.key != null) {
|
||||||
|
blake2b.add(ctx.key!);
|
||||||
|
}
|
||||||
|
blake2b.addUint32(ctx.personalization?.length ?? 0);
|
||||||
|
if (ctx.personalization != null) {
|
||||||
|
blake2b.add(ctx.personalization!);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = blake2b.digest().bytes;
|
||||||
|
for (int i = 0; i < 64; ++i) {
|
||||||
|
hash0[i] = hash[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _expandHash(
|
||||||
|
int digestSize,
|
||||||
|
Uint8List message,
|
||||||
|
Uint8List output,
|
||||||
|
int offset,
|
||||||
|
) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
// Take smaller hash unchanged
|
||||||
|
if (digestSize <= 64) {
|
||||||
|
var blake2b = Blake2bHash(digestSize);
|
||||||
|
blake2b.addUint32(digestSize);
|
||||||
|
blake2b.add(message);
|
||||||
|
var hash = blake2b.digest().bytes;
|
||||||
|
for (i = 0; i < digestSize; ++i, offset++) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, expand to digestSize by repeatedly hashing
|
||||||
|
// and taking the first 32-bytes from the each hash
|
||||||
|
|
||||||
|
var blake2b = Blake2bHash(64);
|
||||||
|
blake2b.addUint32(digestSize);
|
||||||
|
blake2b.add(message);
|
||||||
|
var hash = blake2b.digest().bytes;
|
||||||
|
|
||||||
|
// first block
|
||||||
|
for (i = 0; i < 32; ++i, ++offset) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// subsequent blocks
|
||||||
|
for (j = digestSize - 32; j > 64; j -= 32) {
|
||||||
|
blake2b.reset();
|
||||||
|
blake2b.add(hash);
|
||||||
|
hash = blake2b.digest().bytes;
|
||||||
|
for (i = 0; i < 32; ++i, ++offset) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final block
|
||||||
|
blake2b.reset();
|
||||||
|
blake2b.add(hash);
|
||||||
|
hash = blake2b.digest().bytes;
|
||||||
|
for (i = 0; i < j; ++i, ++offset) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fillSegment(Uint32List buffer, int pass, int slice, int lane) {
|
||||||
|
int refLane, refIndex; // l, z
|
||||||
|
int previous, current;
|
||||||
|
int i, j, startIndex, rand0, rand1;
|
||||||
|
|
||||||
|
bool dataIndependentAddressing = (ctx.type == Argon2Type.argon2i);
|
||||||
|
if (ctx.type == Argon2Type.argon2id) {
|
||||||
|
dataIndependentAddressing = (pass == 0) && (slice < ctx.midSlice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataIndependentAddressing) {
|
||||||
|
_temp[_input + 0] = pass;
|
||||||
|
_temp[_input + 1] = 0;
|
||||||
|
_temp[_input + 2] = lane;
|
||||||
|
_temp[_input + 3] = 0;
|
||||||
|
_temp[_input + 4] = slice;
|
||||||
|
_temp[_input + 5] = 0;
|
||||||
|
_temp[_input + 6] = ctx.blocks;
|
||||||
|
_temp[_input + 7] = ctx.blocks >>> 32;
|
||||||
|
_temp[_input + 8] = ctx.passes;
|
||||||
|
_temp[_input + 9] = 0;
|
||||||
|
_temp[_input + 10] = ctx.type.index;
|
||||||
|
_temp[_input + 11] = 0;
|
||||||
|
_temp[_input + 12] = 0;
|
||||||
|
_temp[_input + 13] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex = 0;
|
||||||
|
if (pass == 0 && slice == 0) {
|
||||||
|
startIndex = 2;
|
||||||
|
if (dataIndependentAddressing) {
|
||||||
|
_increment(_temp, _input + 12);
|
||||||
|
_fillBlock(_temp, prev: _zero, ref: _input, next: _address);
|
||||||
|
_fillBlock(_temp, prev: _zero, ref: _address, next: _address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Offset of the current block */
|
||||||
|
current = lane * ctx.columns + slice * ctx.segments + startIndex;
|
||||||
|
|
||||||
|
if (current % ctx.columns == 0) {
|
||||||
|
/* Last block in this lane */
|
||||||
|
previous = current + ctx.columns - 1;
|
||||||
|
} else {
|
||||||
|
/* Previous block */
|
||||||
|
previous = current - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = startIndex; i < ctx.segments; ++i, ++current, ++previous) {
|
||||||
|
/* 1.1 Rotating prev_offset if needed */
|
||||||
|
if (current % ctx.columns == 1) {
|
||||||
|
previous = current - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2 Computing the index of the reference block */
|
||||||
|
/* 1.2.1 Taking pseudo-random value from the previous block */
|
||||||
|
if (dataIndependentAddressing) {
|
||||||
|
j = i & 0x7F;
|
||||||
|
if (j == 0) {
|
||||||
|
_increment(_temp, _input + 12);
|
||||||
|
_fillBlock(_temp, prev: _zero, ref: _input, next: _address);
|
||||||
|
_fillBlock(_temp, prev: _zero, ref: _address, next: _address);
|
||||||
|
}
|
||||||
|
rand0 = _temp[_address + (j << 1)];
|
||||||
|
rand1 = _temp[_address + (j << 1) + 1];
|
||||||
|
} else {
|
||||||
|
rand0 = buffer[previous << 8];
|
||||||
|
rand1 = buffer[(previous << 8) + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.2 Computing the lane of the reference block */
|
||||||
|
refLane = rand1 % ctx.lanes;
|
||||||
|
|
||||||
|
if (pass == 0 && slice == 0) {
|
||||||
|
/* Can not reference other lanes yet */
|
||||||
|
refLane = lane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.3 Computing the number of possible reference block within the lane */
|
||||||
|
refIndex = _alphaIndex(
|
||||||
|
pass: pass,
|
||||||
|
slice: slice,
|
||||||
|
lane: lane,
|
||||||
|
index: i,
|
||||||
|
random: rand0,
|
||||||
|
sameLane: refLane == lane,
|
||||||
|
);
|
||||||
|
|
||||||
|
/* 2 Creating a new block */
|
||||||
|
_fillBlock(
|
||||||
|
buffer,
|
||||||
|
next: current << 8,
|
||||||
|
prev: previous << 8,
|
||||||
|
ref: (refLane * ctx.columns + refIndex) << 8,
|
||||||
|
/* 1.2.1 v10 and earlier: overwrite, not XOR */
|
||||||
|
xor: ctx.version != Argon2Version.v10 && pass > 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// B[next] ^= G(B[prev], B[ref])
|
||||||
|
/// Fills a new memory block and optionally XORs the old block over the new one.
|
||||||
|
void _fillBlock(
|
||||||
|
Uint32List buffer, {
|
||||||
|
required int prev,
|
||||||
|
required int ref,
|
||||||
|
required int next,
|
||||||
|
bool xor = false,
|
||||||
|
}) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
// T = R = ref ^ prev
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
_blockT[i] = _blockR[i] = buffer[ref + i] ^ buffer[prev + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xor) {
|
||||||
|
// T = ref ^ prev ^ next
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
_blockT[i] ^= buffer[next + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Blake2 on columns of 64-bit words: (0,1,...,15),
|
||||||
|
// then (16,17,..31)... finally (112,113,...127)
|
||||||
|
for (i = j = 0; i < 8; i++, j += 16) {
|
||||||
|
_blake2bMixer(
|
||||||
|
_blockR,
|
||||||
|
(j) << 1,
|
||||||
|
(j + 1) << 1,
|
||||||
|
(j + 2) << 1,
|
||||||
|
(j + 3) << 1,
|
||||||
|
(j + 4) << 1,
|
||||||
|
(j + 5) << 1,
|
||||||
|
(j + 6) << 1,
|
||||||
|
(j + 7) << 1,
|
||||||
|
(j + 8) << 1,
|
||||||
|
(j + 9) << 1,
|
||||||
|
(j + 10) << 1,
|
||||||
|
(j + 11) << 1,
|
||||||
|
(j + 12) << 1,
|
||||||
|
(j + 13) << 1,
|
||||||
|
(j + 14) << 1,
|
||||||
|
(j + 15) << 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113),
|
||||||
|
// then (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127)
|
||||||
|
for (i = j = 0; i < 8; i++, j += 2) {
|
||||||
|
_blake2bMixer(
|
||||||
|
_blockR,
|
||||||
|
(j) << 1,
|
||||||
|
(j + 1) << 1,
|
||||||
|
(j + 16) << 1,
|
||||||
|
(j + 17) << 1,
|
||||||
|
(j + 32) << 1,
|
||||||
|
(j + 33) << 1,
|
||||||
|
(j + 48) << 1,
|
||||||
|
(j + 49) << 1,
|
||||||
|
(j + 64) << 1,
|
||||||
|
(j + 65) << 1,
|
||||||
|
(j + 80) << 1,
|
||||||
|
(j + 81) << 1,
|
||||||
|
(j + 96) << 1,
|
||||||
|
(j + 97) << 1,
|
||||||
|
(j + 112) << 1,
|
||||||
|
(j + 113) << 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// next = T ^ R
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
buffer[next + i] = _blockT[i] ^ _blockR[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _alphaIndex({
|
||||||
|
required int pass,
|
||||||
|
required int slice,
|
||||||
|
required int lane,
|
||||||
|
required int index,
|
||||||
|
required int random,
|
||||||
|
required bool sameLane,
|
||||||
|
}) {
|
||||||
|
int area, pos, start;
|
||||||
|
|
||||||
|
if (pass == 0) {
|
||||||
|
// First pass
|
||||||
|
if (slice == 0) {
|
||||||
|
// First slice
|
||||||
|
area = index - 1; // all but the previous
|
||||||
|
} else if (sameLane) {
|
||||||
|
// The same lane => add current segment
|
||||||
|
area = slice * ctx.segments + index - 1;
|
||||||
|
} else {
|
||||||
|
area = slice * ctx.segments + (index == 0 ? -1 : 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Other passes
|
||||||
|
if (sameLane) {
|
||||||
|
area = ctx.columns - ctx.segments + index - 1;
|
||||||
|
} else {
|
||||||
|
area = ctx.columns - ctx.segments + (index == 0 ? -1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1>
|
||||||
|
// and produce relative position
|
||||||
|
pos = _multiplyAndGetMSB(random, random);
|
||||||
|
pos = area - 1 - _multiplyAndGetMSB(area, pos);
|
||||||
|
|
||||||
|
/* 1.2.5 Computing starting position */
|
||||||
|
start = 0;
|
||||||
|
if (pass != 0 && slice != ctx.slices - 1) {
|
||||||
|
start = (slice + 1) * ctx.segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.6. Computing absolute position */
|
||||||
|
return (start + pos) % ctx.columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `v[i]++`
|
||||||
|
static void _increment(Uint32List v, int i) {
|
||||||
|
if (v[i] == _mask32) {
|
||||||
|
v[i] = 0;
|
||||||
|
v[i + 1]++;
|
||||||
|
} else {
|
||||||
|
v[i]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `((x * y) mod 2^64) >> 32`
|
||||||
|
static int _multiplyAndGetMSB(int x, int y) {
|
||||||
|
int lx, hx, ly, hy;
|
||||||
|
|
||||||
|
hx = (x >>> 16) & _mask16;
|
||||||
|
lx = x & _mask16;
|
||||||
|
hy = (y >>> 16) & _mask16;
|
||||||
|
ly = y & _mask16;
|
||||||
|
|
||||||
|
return ((hy * hx) + ((lx * hy + hx * ly) >>> 16)) & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `v[x] += v[y] + 2 * ((v[x] & _mask32) * (v[y] & _mask32))`
|
||||||
|
static void _fBlaMka(Uint32List v, int x, int y) {
|
||||||
|
var t = (BigInt.from(v[x]) * BigInt.from(v[y])) << 1;
|
||||||
|
t += (BigInt.from(v[x + 1]) << 32) + BigInt.from(v[x]);
|
||||||
|
t += (BigInt.from(v[y + 1]) << 32) + BigInt.from(v[y]);
|
||||||
|
|
||||||
|
v[x] = t.toUnsigned(32).toInt();
|
||||||
|
v[x + 1] = (t >> 32).toUnsigned(32).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// v[k] = (v[i] << (64 - n)) | (v[i] >>> n)
|
||||||
|
static void _rotr(int n, List<int> v, int i, int k) {
|
||||||
|
var a = v[i + 1];
|
||||||
|
var b = v[i];
|
||||||
|
if (n == 32) {
|
||||||
|
v[k + 1] = b;
|
||||||
|
v[k] = a;
|
||||||
|
} else if (n < 32) {
|
||||||
|
v[k + 1] = (b << (32 - n)) | (a >>> n);
|
||||||
|
v[k] = (a << (32 - n)) | (b >>> n);
|
||||||
|
} else {
|
||||||
|
v[k + 1] = (a << (64 - n)) | (b >>> (n - 32));
|
||||||
|
v[k] = (b << (64 - n)) | (a >>> (n - 32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `v[k] = v[i] ^ v[j]`
|
||||||
|
static void _xor(List<int> v, int i, int j, int k) {
|
||||||
|
v[k] = v[i] ^ v[j];
|
||||||
|
v[k + 1] = v[i + 1] ^ v[j + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _mix(Uint32List v, int a, int b, int c, int d) {
|
||||||
|
_fBlaMka(v, a, b);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], 32);
|
||||||
|
_xor(v, d, a, d);
|
||||||
|
_rotr(32, v, d, d);
|
||||||
|
|
||||||
|
_fBlaMka(v, c, d);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], 24);
|
||||||
|
_xor(v, b, c, b);
|
||||||
|
_rotr(24, v, b, b);
|
||||||
|
|
||||||
|
_fBlaMka(v, a, b);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], 16);
|
||||||
|
_xor(v, d, a, d);
|
||||||
|
_rotr(16, v, d, d);
|
||||||
|
|
||||||
|
_fBlaMka(v, c, d);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], 63);
|
||||||
|
_xor(v, b, c, b);
|
||||||
|
_rotr(63, v, b, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _blake2bMixer(
|
||||||
|
Uint32List v,
|
||||||
|
int v0,
|
||||||
|
int v1,
|
||||||
|
int v2,
|
||||||
|
int v3,
|
||||||
|
int v4,
|
||||||
|
int v5,
|
||||||
|
int v6,
|
||||||
|
int v7,
|
||||||
|
int v8,
|
||||||
|
int v9,
|
||||||
|
int v10,
|
||||||
|
int v11,
|
||||||
|
int v12,
|
||||||
|
int v13,
|
||||||
|
int v14,
|
||||||
|
int v15,
|
||||||
|
) {
|
||||||
|
_mix(v, v0, v4, v8, v12);
|
||||||
|
_mix(v, v1, v5, v9, v13);
|
||||||
|
_mix(v, v2, v6, v10, v14);
|
||||||
|
_mix(v, v3, v7, v11, v15);
|
||||||
|
_mix(v, v0, v5, v10, v15);
|
||||||
|
_mix(v, v1, v6, v11, v12);
|
||||||
|
_mix(v, v2, v7, v8, v13);
|
||||||
|
_mix(v, v3, v4, v9, v14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on Blake2bHash {
|
||||||
|
void addUint32(int value) {
|
||||||
|
add([
|
||||||
|
value,
|
||||||
|
value >>> 8,
|
||||||
|
value >>> 16,
|
||||||
|
value >>> 24,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
591
hashlib/lib/src/algorithms/argon2/argon2_64bit.dart
Normal file
591
hashlib/lib/src/algorithms/argon2/argon2_64bit.dart
Normal file
|
|
@ -0,0 +1,591 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'common.dart';
|
||||||
|
import '../blake2/blake2b_64bit.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
// slice 0 slice 1 slice 2 slice 3
|
||||||
|
// ____/\____ ____/\____ ____/\____ ____/\____
|
||||||
|
// / \ / \ / \ / \
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | segment 0 | segment 1 | segment 2 | segment 3 | -> lane 0
|
||||||
|
// +------------+------------+------------+-----------+
|
||||||
|
// | segment 4 | segment 5 | segment 6 | segment 7 | -> lane 1
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | segment 8 | segment 9 | segment 10 | segment 11 | -> lane 2
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | ... ... ... | ...
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
// | | | | | -> lane p - 1
|
||||||
|
// +------------+------------+------------+------------+
|
||||||
|
|
||||||
|
class Argon2Internal {
|
||||||
|
final Argon2Context ctx;
|
||||||
|
final _hash0 = Uint8List(64 + 8);
|
||||||
|
final _blockR = Uint64List(128);
|
||||||
|
final _blockT = Uint64List(128);
|
||||||
|
final _input = Uint64List(128);
|
||||||
|
final _address = Uint64List(128);
|
||||||
|
|
||||||
|
late final _digest = Uint8List(ctx.hashLength);
|
||||||
|
late final _memory = Uint64List(ctx.blocks << 7);
|
||||||
|
|
||||||
|
Argon2Internal(this.ctx);
|
||||||
|
|
||||||
|
Uint8List convert(List<int> password) {
|
||||||
|
int i, j, k, cols;
|
||||||
|
int pass, slice, lane;
|
||||||
|
var hash0_32 = Uint32List.view(_hash0.buffer);
|
||||||
|
var memoryBytes = Uint8List.view(_memory.buffer);
|
||||||
|
|
||||||
|
// H_0 Generation (64 + 8 = 72 bytes)
|
||||||
|
_initialHash(_hash0, password);
|
||||||
|
|
||||||
|
// Initial block generation: First Lane Blocks
|
||||||
|
k = 0;
|
||||||
|
hash0_32[16] = 0;
|
||||||
|
cols = ctx.columns << 10;
|
||||||
|
for (i = 0; i < ctx.lanes; i++, k += cols) {
|
||||||
|
// B[i][0] = H'^(1024)(H_0 || LE32(0) || LE32(i))
|
||||||
|
hash0_32[17] = i;
|
||||||
|
_expandHash(1024, _hash0, memoryBytes, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial block generation: Second Lane Blocks
|
||||||
|
k = 1024;
|
||||||
|
hash0_32[16] = 1;
|
||||||
|
for (i = 0; i < ctx.lanes; i++, k += cols) {
|
||||||
|
// B[i][1] = H'^(1024)(H_0 || LE32(1) || LE32(i))
|
||||||
|
hash0_32[17] = i;
|
||||||
|
_expandHash(1024, _hash0, memoryBytes, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Further block generation
|
||||||
|
for (pass = 0; pass < ctx.passes; ++pass) {
|
||||||
|
for (slice = 0; slice < ctx.slices; ++slice) {
|
||||||
|
for (lane = 0; lane < ctx.lanes; ++lane) {
|
||||||
|
_fillSegment(pass, slice, lane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalization : XOR the last column blocks
|
||||||
|
j = cols - 1024;
|
||||||
|
var block = Uint8List.view(memoryBytes.buffer, j, 1024);
|
||||||
|
for (k = 1; k < ctx.lanes; ++k) {
|
||||||
|
j += cols;
|
||||||
|
for (i = 0; i < 1024; ++i) {
|
||||||
|
block[i] ^= memoryBytes[j + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the block to make final result
|
||||||
|
_expandHash(ctx.hashLength, block, _digest, 0);
|
||||||
|
return _digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initialHash(Uint8List hash0, List<int> password) {
|
||||||
|
// H_0 = H^(64)(LE32(p) || LE32(T) || LE32(m) || LE32(t) ||
|
||||||
|
// LE32(v) || LE32(y) || LE32(length(P)) || P ||
|
||||||
|
// LE32(length(S)) || S || LE32(length(K)) || K ||
|
||||||
|
// LE32(length(X)) || X)
|
||||||
|
var blake2b = Blake2bHash(64);
|
||||||
|
blake2b.addUint32(ctx.lanes);
|
||||||
|
blake2b.addUint32(ctx.hashLength);
|
||||||
|
blake2b.addUint32(ctx.memorySizeKB);
|
||||||
|
blake2b.addUint32(ctx.passes);
|
||||||
|
blake2b.addUint32(ctx.version.value);
|
||||||
|
blake2b.addUint32(ctx.type.index);
|
||||||
|
blake2b.addUint32(password.length);
|
||||||
|
blake2b.add(password);
|
||||||
|
blake2b.addUint32(ctx.salt.length);
|
||||||
|
blake2b.add(ctx.salt);
|
||||||
|
blake2b.addUint32(ctx.key?.length ?? 0);
|
||||||
|
if (ctx.key != null) {
|
||||||
|
blake2b.add(ctx.key!);
|
||||||
|
}
|
||||||
|
blake2b.addUint32(ctx.personalization?.length ?? 0);
|
||||||
|
if (ctx.personalization != null) {
|
||||||
|
blake2b.add(ctx.personalization!);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = blake2b.digest().bytes;
|
||||||
|
for (int i = 0; i < 64; ++i) {
|
||||||
|
hash0[i] = hash[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _expandHash(
|
||||||
|
int digestSize,
|
||||||
|
Uint8List message,
|
||||||
|
Uint8List output,
|
||||||
|
int offset,
|
||||||
|
) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
// Take smaller hash unchanged
|
||||||
|
if (digestSize <= 64) {
|
||||||
|
var blake2b = Blake2bHash(digestSize);
|
||||||
|
blake2b.addUint32(digestSize);
|
||||||
|
blake2b.add(message);
|
||||||
|
var hash = blake2b.digest().bytes;
|
||||||
|
for (i = 0; i < digestSize; ++i, offset++) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, expand to digestSize by repeatedly hashing
|
||||||
|
// and taking the first 32-bytes from the each hash
|
||||||
|
|
||||||
|
var blake2b = Blake2bHash(64);
|
||||||
|
blake2b.addUint32(digestSize);
|
||||||
|
blake2b.add(message);
|
||||||
|
var hash = blake2b.digest().bytes;
|
||||||
|
|
||||||
|
// first block
|
||||||
|
for (i = 0; i < 32; ++i, ++offset) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// subsequent blocks
|
||||||
|
for (j = digestSize - 32; j > 64; j -= 32) {
|
||||||
|
blake2b.reset();
|
||||||
|
blake2b.add(hash);
|
||||||
|
hash = blake2b.digest().bytes;
|
||||||
|
for (i = 0; i < 32; ++i, ++offset) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final block
|
||||||
|
blake2b.reset();
|
||||||
|
blake2b.add(hash);
|
||||||
|
hash = blake2b.digest().bytes;
|
||||||
|
for (i = 0; i < j; ++i, ++offset) {
|
||||||
|
output[offset] = hash[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fillSegment(int pass, int slice, int lane) {
|
||||||
|
int refLane, refIndex; // l, z
|
||||||
|
int previous, current;
|
||||||
|
int i, startIndex, random;
|
||||||
|
|
||||||
|
bool xor = ctx.version != Argon2Version.v10 && pass > 0;
|
||||||
|
bool useAddress = (ctx.type == Argon2Type.argon2i);
|
||||||
|
if (ctx.type == Argon2Type.argon2id) {
|
||||||
|
useAddress = (pass == 0) && (slice < ctx.midSlice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useAddress) {
|
||||||
|
_input[0] = pass;
|
||||||
|
_input[1] = lane;
|
||||||
|
_input[2] = slice;
|
||||||
|
_input[3] = ctx.blocks;
|
||||||
|
_input[4] = ctx.passes;
|
||||||
|
_input[5] = ctx.type.index;
|
||||||
|
_input[6] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex = 0;
|
||||||
|
if (pass == 0 && slice == 0) {
|
||||||
|
startIndex = 2;
|
||||||
|
if (useAddress) {
|
||||||
|
_input[6]++;
|
||||||
|
_nextAddress(_input, _address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Offset of the current block */
|
||||||
|
current = lane * ctx.columns + slice * ctx.segments + startIndex;
|
||||||
|
|
||||||
|
for (i = startIndex; i < ctx.segments; ++i, ++current) {
|
||||||
|
if (current % ctx.columns == 0) {
|
||||||
|
/* Last block in this lane */
|
||||||
|
previous = current + ctx.columns - 1;
|
||||||
|
} else {
|
||||||
|
/* Previous block */
|
||||||
|
previous = current - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2 Computing the index of the reference block */
|
||||||
|
/* 1.2.1 Taking pseudo-random value from the previous block */
|
||||||
|
if (useAddress) {
|
||||||
|
if (i & 0x7F == 0) {
|
||||||
|
_input[6]++;
|
||||||
|
_nextAddress(_input, _address);
|
||||||
|
}
|
||||||
|
random = _address[i & 0x7F];
|
||||||
|
} else {
|
||||||
|
random = _memory[previous << 7];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.2 Computing the lane of the reference block */
|
||||||
|
refLane = (random >>> 32) % ctx.lanes;
|
||||||
|
|
||||||
|
if (pass == 0 && slice == 0) {
|
||||||
|
/* Can not reference other lanes yet */
|
||||||
|
refLane = lane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.3 Computing the number of possible reference block within the lane */
|
||||||
|
refIndex = _alphaIndex(
|
||||||
|
random: random & _mask32,
|
||||||
|
index: i,
|
||||||
|
slice: slice,
|
||||||
|
lane: lane,
|
||||||
|
pass: pass,
|
||||||
|
sameLane: refLane == lane,
|
||||||
|
);
|
||||||
|
|
||||||
|
/* 2 Creating a new block */
|
||||||
|
_fillBlock(
|
||||||
|
_memory,
|
||||||
|
xor: xor,
|
||||||
|
next: current << 7,
|
||||||
|
prev: previous << 7,
|
||||||
|
ref: (refLane * ctx.columns + refIndex) << 7,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _alphaIndex({
|
||||||
|
required int pass,
|
||||||
|
required int slice,
|
||||||
|
required int lane,
|
||||||
|
required int index,
|
||||||
|
required int random,
|
||||||
|
required bool sameLane,
|
||||||
|
}) {
|
||||||
|
int area, pos, start;
|
||||||
|
|
||||||
|
if (pass == 0) {
|
||||||
|
// First pass
|
||||||
|
if (slice == 0) {
|
||||||
|
// First slice
|
||||||
|
area = index - 1; // all but the previous
|
||||||
|
} else if (sameLane) {
|
||||||
|
// The same lane => add current segment
|
||||||
|
area = slice * ctx.segments + index - 1;
|
||||||
|
} else if (index == 0) {
|
||||||
|
area = slice * ctx.segments - 1;
|
||||||
|
} else {
|
||||||
|
area = slice * ctx.segments;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Other passes
|
||||||
|
if (sameLane) {
|
||||||
|
area = ctx.columns - ctx.segments + index - 1;
|
||||||
|
} else if (index == 0) {
|
||||||
|
area = ctx.columns - ctx.segments - 1;
|
||||||
|
} else {
|
||||||
|
area = ctx.columns - ctx.segments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1>
|
||||||
|
// and produce relative position
|
||||||
|
pos = (random * random) >>> 32;
|
||||||
|
pos = area - 1 - ((area * pos) >>> 32);
|
||||||
|
|
||||||
|
/* 1.2.5 Computing starting position */
|
||||||
|
start = 0;
|
||||||
|
if (pass != 0 && slice != ctx.slices - 1) {
|
||||||
|
start = (slice + 1) * ctx.segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.6. Computing absolute position */
|
||||||
|
return (start + pos) % ctx.columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills a memory block and optionally XORs the old block over it.
|
||||||
|
void _nextAddress(Uint64List input, Uint64List address) {
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
_blockR[i] = address[i] = input[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = 0; k < 2; ++k) {
|
||||||
|
// Apply Blake2 on columns of 64-bit words: (0,1,...,15),
|
||||||
|
// then (16,17,..31)... finally (112,113,...127)
|
||||||
|
for (int j = 0; j < 128; j += 16) {
|
||||||
|
_blake2bMixer(
|
||||||
|
_blockR,
|
||||||
|
j,
|
||||||
|
j + 1,
|
||||||
|
j + 2,
|
||||||
|
j + 3,
|
||||||
|
j + 4,
|
||||||
|
j + 5,
|
||||||
|
j + 6,
|
||||||
|
j + 7,
|
||||||
|
j + 8,
|
||||||
|
j + 9,
|
||||||
|
j + 10,
|
||||||
|
j + 11,
|
||||||
|
j + 12,
|
||||||
|
j + 13,
|
||||||
|
j + 14,
|
||||||
|
j + 15,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113),
|
||||||
|
// then (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127)
|
||||||
|
for (int j = 0; j < 16; j += 2) {
|
||||||
|
_blake2bMixer(
|
||||||
|
_blockR,
|
||||||
|
j,
|
||||||
|
j + 1,
|
||||||
|
j + 16,
|
||||||
|
j + 17,
|
||||||
|
j + 32,
|
||||||
|
j + 33,
|
||||||
|
j + 48,
|
||||||
|
j + 49,
|
||||||
|
j + 64,
|
||||||
|
j + 65,
|
||||||
|
j + 80,
|
||||||
|
j + 81,
|
||||||
|
j + 96,
|
||||||
|
j + 97,
|
||||||
|
j + 112,
|
||||||
|
j + 113,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
address[i] = _blockR[i] ^= address[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills a memory block and optionally XORs the old block over it.
|
||||||
|
void _fillBlock(
|
||||||
|
Uint64List memory, {
|
||||||
|
required int prev,
|
||||||
|
required int ref,
|
||||||
|
required int next,
|
||||||
|
bool xor = false,
|
||||||
|
}) {
|
||||||
|
// R = ref ^ prev
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
_blockT[i] = _blockR[i] = memory[ref + i] ^ memory[prev + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xor) {
|
||||||
|
// T ^= next
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
_blockT[i] ^= memory[next + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Blake2 on columns of 64-bit words: (0,1,...,15),
|
||||||
|
// then (16,17,..31)... finally (112,113,...127)
|
||||||
|
for (int j = 0; j < 128; j += 16) {
|
||||||
|
_blake2bMixer(
|
||||||
|
_blockR,
|
||||||
|
j,
|
||||||
|
j + 1,
|
||||||
|
j + 2,
|
||||||
|
j + 3,
|
||||||
|
j + 4,
|
||||||
|
j + 5,
|
||||||
|
j + 6,
|
||||||
|
j + 7,
|
||||||
|
j + 8,
|
||||||
|
j + 9,
|
||||||
|
j + 10,
|
||||||
|
j + 11,
|
||||||
|
j + 12,
|
||||||
|
j + 13,
|
||||||
|
j + 14,
|
||||||
|
j + 15,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113),
|
||||||
|
// then (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127)
|
||||||
|
for (int j = 0; j < 16; j += 2) {
|
||||||
|
_blake2bMixer(
|
||||||
|
_blockR,
|
||||||
|
j,
|
||||||
|
j + 1,
|
||||||
|
j + 16,
|
||||||
|
j + 17,
|
||||||
|
j + 32,
|
||||||
|
j + 33,
|
||||||
|
j + 48,
|
||||||
|
j + 49,
|
||||||
|
j + 64,
|
||||||
|
j + 65,
|
||||||
|
j + 80,
|
||||||
|
j + 81,
|
||||||
|
j + 96,
|
||||||
|
j + 97,
|
||||||
|
j + 112,
|
||||||
|
j + 113,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// next = T ^ R
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
memory[next + i] = _blockR[i] ^ _blockT[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _mul32(int a, int b) => (a & _mask32) * (b & _mask32);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotr(int x, int n) => (x >>> n) ^ (x << (64 - n));
|
||||||
|
|
||||||
|
static void _blake2bMixer(
|
||||||
|
Uint64List v,
|
||||||
|
int i0,
|
||||||
|
int i1,
|
||||||
|
int i2,
|
||||||
|
int i3,
|
||||||
|
int i4,
|
||||||
|
int i5,
|
||||||
|
int i6,
|
||||||
|
int i7,
|
||||||
|
int i8,
|
||||||
|
int i9,
|
||||||
|
int i10,
|
||||||
|
int i11,
|
||||||
|
int i12,
|
||||||
|
int i13,
|
||||||
|
int i14,
|
||||||
|
int i15,
|
||||||
|
) {
|
||||||
|
int v0 = v[i0];
|
||||||
|
int v1 = v[i1];
|
||||||
|
int v2 = v[i2];
|
||||||
|
int v3 = v[i3];
|
||||||
|
int v4 = v[i4];
|
||||||
|
int v5 = v[i5];
|
||||||
|
int v6 = v[i6];
|
||||||
|
int v7 = v[i7];
|
||||||
|
int v8 = v[i8];
|
||||||
|
int v9 = v[i9];
|
||||||
|
int v10 = v[i10];
|
||||||
|
int v11 = v[i11];
|
||||||
|
int v12 = v[i12];
|
||||||
|
int v13 = v[i13];
|
||||||
|
int v14 = v[i14];
|
||||||
|
int v15 = v[i15];
|
||||||
|
|
||||||
|
// _mix(v, v0, v4, v8, v12);
|
||||||
|
v0 += v4 + (_mul32(v0, v4) << 1);
|
||||||
|
v12 = _rotr(v12 ^ v0, 32);
|
||||||
|
v8 += v12 + (_mul32(v8, v12) << 1);
|
||||||
|
v4 = _rotr(v4 ^ v8, 24);
|
||||||
|
v0 += v4 + (_mul32(v0, v4) << 1);
|
||||||
|
v12 = _rotr(v12 ^ v0, 16);
|
||||||
|
v8 += v12 + (_mul32(v8, v12) << 1);
|
||||||
|
v4 = _rotr(v4 ^ v8, 63);
|
||||||
|
|
||||||
|
// _mix(v, v1, v5, v9, v13);
|
||||||
|
v1 += v5 + (_mul32(v1, v5) << 1);
|
||||||
|
v13 = _rotr(v13 ^ v1, 32);
|
||||||
|
v9 += v13 + (_mul32(v9, v13) << 1);
|
||||||
|
v5 = _rotr(v5 ^ v9, 24);
|
||||||
|
v1 += v5 + (_mul32(v1, v5) << 1);
|
||||||
|
v13 = _rotr(v13 ^ v1, 16);
|
||||||
|
v9 += v13 + (_mul32(v9, v13) << 1);
|
||||||
|
v5 = _rotr(v5 ^ v9, 63);
|
||||||
|
|
||||||
|
// _mix(v, v2, v6, v10, v14);
|
||||||
|
v2 += v6 + (_mul32(v2, v6) << 1);
|
||||||
|
v14 = _rotr(v14 ^ v2, 32);
|
||||||
|
v10 += v14 + (_mul32(v10, v14) << 1);
|
||||||
|
v6 = _rotr(v6 ^ v10, 24);
|
||||||
|
v2 += v6 + (_mul32(v2, v6) << 1);
|
||||||
|
v14 = _rotr(v14 ^ v2, 16);
|
||||||
|
v10 += v14 + (_mul32(v10, v14) << 1);
|
||||||
|
v6 = _rotr(v6 ^ v10, 63);
|
||||||
|
|
||||||
|
// _mix(v, v3, v7, v11, v15);
|
||||||
|
v3 += v7 + (_mul32(v3, v7) << 1);
|
||||||
|
v15 = _rotr(v15 ^ v3, 32);
|
||||||
|
v11 += v15 + (_mul32(v11, v15) << 1);
|
||||||
|
v7 = _rotr(v7 ^ v11, 24);
|
||||||
|
v3 += v7 + (_mul32(v3, v7) << 1);
|
||||||
|
v15 = _rotr(v15 ^ v3, 16);
|
||||||
|
v11 += v15 + (_mul32(v11, v15) << 1);
|
||||||
|
v7 = _rotr(v7 ^ v11, 63);
|
||||||
|
|
||||||
|
// _mix(v, v0, v5, v10, v15);
|
||||||
|
v0 += v5 + (_mul32(v0, v5) << 1);
|
||||||
|
v15 = _rotr(v15 ^ v0, 32);
|
||||||
|
v10 += v15 + (_mul32(v10, v15) << 1);
|
||||||
|
v5 = _rotr(v5 ^ v10, 24);
|
||||||
|
v0 += v5 + (_mul32(v0, v5) << 1);
|
||||||
|
v15 = _rotr(v15 ^ v0, 16);
|
||||||
|
v10 += v15 + (_mul32(v10, v15) << 1);
|
||||||
|
v5 = _rotr(v5 ^ v10, 63);
|
||||||
|
|
||||||
|
// _mix(v, v1, v6, v11, v12);
|
||||||
|
v1 += v6 + (_mul32(v1, v6) << 1);
|
||||||
|
v12 = _rotr(v12 ^ v1, 32);
|
||||||
|
v11 += v12 + (_mul32(v11, v12) << 1);
|
||||||
|
v6 = _rotr(v6 ^ v11, 24);
|
||||||
|
v1 += v6 + (_mul32(v1, v6) << 1);
|
||||||
|
v12 = _rotr(v12 ^ v1, 16);
|
||||||
|
v11 += v12 + (_mul32(v11, v12) << 1);
|
||||||
|
v6 = _rotr(v6 ^ v11, 63);
|
||||||
|
|
||||||
|
// _mix(v, v2, v7, v8, v13);
|
||||||
|
v2 += v7 + (_mul32(v2, v7) << 1);
|
||||||
|
v13 = _rotr(v13 ^ v2, 32);
|
||||||
|
v8 += v13 + (_mul32(v8, v13) << 1);
|
||||||
|
v7 = _rotr(v7 ^ v8, 24);
|
||||||
|
v2 += v7 + (_mul32(v2, v7) << 1);
|
||||||
|
v13 = _rotr(v13 ^ v2, 16);
|
||||||
|
v8 += v13 + (_mul32(v8, v13) << 1);
|
||||||
|
v7 = _rotr(v7 ^ v8, 63);
|
||||||
|
|
||||||
|
// _mix(v, v3, v4, v9, v14);
|
||||||
|
v3 += v4 + (_mul32(v3, v4) << 1);
|
||||||
|
v14 = _rotr(v14 ^ v3, 32);
|
||||||
|
v9 += v14 + (_mul32(v9, v14) << 1);
|
||||||
|
v4 = _rotr(v4 ^ v9, 24);
|
||||||
|
v3 += v4 + (_mul32(v3, v4) << 1);
|
||||||
|
v14 = _rotr(v14 ^ v3, 16);
|
||||||
|
v9 += v14 + (_mul32(v9, v14) << 1);
|
||||||
|
v4 = _rotr(v4 ^ v9, 63);
|
||||||
|
|
||||||
|
v[i0] = v0;
|
||||||
|
v[i1] = v1;
|
||||||
|
v[i2] = v2;
|
||||||
|
v[i3] = v3;
|
||||||
|
v[i4] = v4;
|
||||||
|
v[i5] = v5;
|
||||||
|
v[i6] = v6;
|
||||||
|
v[i7] = v7;
|
||||||
|
v[i8] = v8;
|
||||||
|
v[i9] = v9;
|
||||||
|
v[i10] = v10;
|
||||||
|
v[i11] = v11;
|
||||||
|
v[i12] = v12;
|
||||||
|
v[i13] = v13;
|
||||||
|
v[i14] = v14;
|
||||||
|
v[i15] = v15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on Blake2bHash {
|
||||||
|
void addUint32(int value) {
|
||||||
|
add([
|
||||||
|
value,
|
||||||
|
value >>> 8,
|
||||||
|
value >>> 16,
|
||||||
|
value >>> 24,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
298
hashlib/lib/src/algorithms/argon2/common.dart
Normal file
298
hashlib/lib/src/algorithms/argon2/common.dart
Normal file
|
|
@ -0,0 +1,298 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
import 'package:hashlib/src/core/hash_digest.dart';
|
||||||
|
import 'package:hashlib/src/random.dart';
|
||||||
|
|
||||||
|
const int _slices = 4;
|
||||||
|
const int _minParallelism = 1;
|
||||||
|
const int _maxParallelism = 0x7FFF;
|
||||||
|
const int _minDigestSize = 4;
|
||||||
|
const int _maxDigestSize = 0x3FFFFFF;
|
||||||
|
const int _minIterations = 1;
|
||||||
|
const int _maxIterations = 0x3FFFFFF;
|
||||||
|
const int _maxMemory = 0x3FFFFFF;
|
||||||
|
const int _minSaltSize = 8;
|
||||||
|
const int _maxSaltSize = 0x3FFFFFF;
|
||||||
|
const int _minKeySize = 1;
|
||||||
|
const int _maxKeySize = 0x3FFFFFF;
|
||||||
|
const int _minAD = 1;
|
||||||
|
const int _maxAD = 0x3FFFFFF;
|
||||||
|
const int _defaultHashLength = 32;
|
||||||
|
|
||||||
|
/// The Argon2 types
|
||||||
|
enum Argon2Type {
|
||||||
|
argon2d._('argon2d'),
|
||||||
|
argon2i._('argon2i'),
|
||||||
|
argon2id._('argon2id');
|
||||||
|
|
||||||
|
/// Name of this type
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
const Argon2Type._(this.name);
|
||||||
|
|
||||||
|
/// Gets the type from string name
|
||||||
|
static Argon2Type fromName(String name) {
|
||||||
|
switch (name) {
|
||||||
|
case 'argon2d':
|
||||||
|
return Argon2Type.argon2d;
|
||||||
|
case 'argon2i':
|
||||||
|
return Argon2Type.argon2i;
|
||||||
|
case 'argon2id':
|
||||||
|
return Argon2Type.argon2id;
|
||||||
|
default:
|
||||||
|
throw ArgumentError('Unknown type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Argon2 versions
|
||||||
|
enum Argon2Version {
|
||||||
|
v10._(0x10),
|
||||||
|
v13._(0x13);
|
||||||
|
|
||||||
|
/// The version value
|
||||||
|
final int value;
|
||||||
|
|
||||||
|
const Argon2Version._(this.value);
|
||||||
|
|
||||||
|
/// Gets the version from integer value
|
||||||
|
static Argon2Version fromValue(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0x10:
|
||||||
|
return Argon2Version.v10;
|
||||||
|
case 0x13:
|
||||||
|
return Argon2Version.v13;
|
||||||
|
default:
|
||||||
|
throw ArgumentError('Unknown version');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The HashDigest for Argon2 with [Argon2Context]
|
||||||
|
class Argon2HashDigest extends HashDigest {
|
||||||
|
final Argon2Context ctx;
|
||||||
|
|
||||||
|
const Argon2HashDigest(this.ctx, super.bytes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => encoded();
|
||||||
|
|
||||||
|
/// Gets the PHC-compliant string for this [Argon2HashDigest]
|
||||||
|
String encoded() => ctx.toEncoded(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The configuration used by the **Argon2** algorithm
|
||||||
|
class Argon2Context {
|
||||||
|
/// Argon2 Hash Type
|
||||||
|
final Argon2Type type;
|
||||||
|
|
||||||
|
/// The current version is 0x13 (decimal: 19)
|
||||||
|
final Argon2Version version;
|
||||||
|
|
||||||
|
/// Degree of parallelism (i.e. number of threads)
|
||||||
|
final int lanes;
|
||||||
|
|
||||||
|
/// Desired number of returned bytes
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
/// Amount of memory (in kibibytes) to use
|
||||||
|
final int memorySizeKB;
|
||||||
|
|
||||||
|
/// Number of iterations to perform
|
||||||
|
final int passes;
|
||||||
|
|
||||||
|
/// Salt (16 bytes recommended for password hashing)
|
||||||
|
final List<int> salt;
|
||||||
|
|
||||||
|
/// Optional key
|
||||||
|
final List<int>? key;
|
||||||
|
|
||||||
|
/// Optional arbitrary additional data
|
||||||
|
final List<int>? personalization;
|
||||||
|
|
||||||
|
/// Number of slices per column
|
||||||
|
final int slices;
|
||||||
|
|
||||||
|
/// The start index of the second half of the slices
|
||||||
|
final int midSlice;
|
||||||
|
|
||||||
|
/// Number of segments per lane
|
||||||
|
final int segments;
|
||||||
|
|
||||||
|
/// Total number of columns per lane
|
||||||
|
final int columns;
|
||||||
|
|
||||||
|
/// Total number of memory blocks (1024 bytes each)
|
||||||
|
final int blocks;
|
||||||
|
|
||||||
|
const Argon2Context._({
|
||||||
|
required this.salt,
|
||||||
|
required this.version,
|
||||||
|
required this.type,
|
||||||
|
required this.hashLength,
|
||||||
|
required this.passes,
|
||||||
|
required this.lanes,
|
||||||
|
required this.memorySizeKB,
|
||||||
|
required this.slices,
|
||||||
|
required this.segments,
|
||||||
|
required this.columns,
|
||||||
|
required this.blocks,
|
||||||
|
required this.key,
|
||||||
|
required this.personalization,
|
||||||
|
}) : midSlice = slices ~/ 2;
|
||||||
|
|
||||||
|
/// Creates a context for Argon2 password hashing
|
||||||
|
///
|
||||||
|
/// Required Parameters:
|
||||||
|
/// - [iterations] Number of iterations to perform.
|
||||||
|
/// - [parallelism] Degree of parallelism (i.e. number of threads).
|
||||||
|
/// - [memorySizeKB] Amount of memory (in kibibytes) to use.
|
||||||
|
///
|
||||||
|
/// Optional Parameters:
|
||||||
|
/// - [salt] Salt (16 bytes recommended for password hashing). If absent, a
|
||||||
|
/// 64 bytes random salt is generated.
|
||||||
|
/// - [hashLength] Desired number of returned bytes. Default: 32.
|
||||||
|
/// - [key] Additional key.
|
||||||
|
/// - [personalization] Arbitrary additional data.
|
||||||
|
/// - [version] Algorithm version; Default: `Argon2Version.v13`,
|
||||||
|
/// - [type] Argon2 type; Default: `Argon2Type.argon2id`.
|
||||||
|
factory Argon2Context({
|
||||||
|
required int iterations,
|
||||||
|
required int parallelism,
|
||||||
|
required int memorySizeKB,
|
||||||
|
List<int>? key,
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? personalization,
|
||||||
|
int? hashLength,
|
||||||
|
Argon2Version version = Argon2Version.v13,
|
||||||
|
Argon2Type type = Argon2Type.argon2id,
|
||||||
|
}) {
|
||||||
|
hashLength ??= _defaultHashLength;
|
||||||
|
if (hashLength < _minDigestSize) {
|
||||||
|
throw ArgumentError('The tag length must be at least $_minDigestSize');
|
||||||
|
}
|
||||||
|
if (hashLength > _maxDigestSize) {
|
||||||
|
throw ArgumentError('The tag length must be at most $_maxDigestSize');
|
||||||
|
}
|
||||||
|
if (parallelism < _minParallelism) {
|
||||||
|
throw ArgumentError('The parallelism must be at least $_minParallelism');
|
||||||
|
}
|
||||||
|
if (parallelism > _maxParallelism) {
|
||||||
|
throw ArgumentError('The parallelism must be at most $_maxParallelism');
|
||||||
|
}
|
||||||
|
if (iterations < _minIterations) {
|
||||||
|
throw ArgumentError('The iterations must be at least $_minIterations');
|
||||||
|
}
|
||||||
|
if (iterations > _maxIterations) {
|
||||||
|
throw ArgumentError('The iterations must be at most $_maxIterations');
|
||||||
|
}
|
||||||
|
if (memorySizeKB < (parallelism << 3)) {
|
||||||
|
throw ArgumentError('The memory size must be at least 8 * parallelism');
|
||||||
|
}
|
||||||
|
if (memorySizeKB > _maxMemory) {
|
||||||
|
throw ArgumentError('The memorySizeKB must be at most $_maxMemory');
|
||||||
|
}
|
||||||
|
salt ??= randomBytes(64);
|
||||||
|
if (salt.length < _minSaltSize) {
|
||||||
|
throw ArgumentError('The salt must be at least $_minSaltSize bytes long');
|
||||||
|
}
|
||||||
|
if (salt.length > _maxSaltSize) {
|
||||||
|
throw ArgumentError('The salt must be at most $_maxSaltSize bytes long');
|
||||||
|
}
|
||||||
|
if (key != null) {
|
||||||
|
if (key.length < _minKeySize) {
|
||||||
|
throw ArgumentError('The key must be at least $_minKeySize bytes long');
|
||||||
|
}
|
||||||
|
if (key.length > _maxKeySize) {
|
||||||
|
throw ArgumentError('The key must be at most $_maxKeySize bytes long');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (personalization != null) {
|
||||||
|
if (personalization.length < _minAD) {
|
||||||
|
throw ArgumentError('The extra data must be at least $_minAD bytes');
|
||||||
|
}
|
||||||
|
if (personalization.length > _maxAD) {
|
||||||
|
throw ArgumentError('The extra data must be at most $_maxAD');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int segments = memorySizeKB ~/ (_slices * parallelism);
|
||||||
|
int columns = _slices * segments;
|
||||||
|
int blocks = parallelism * _slices * segments;
|
||||||
|
|
||||||
|
return Argon2Context._(
|
||||||
|
salt: salt,
|
||||||
|
version: version,
|
||||||
|
type: type,
|
||||||
|
hashLength: hashLength,
|
||||||
|
passes: iterations,
|
||||||
|
slices: _slices,
|
||||||
|
lanes: parallelism,
|
||||||
|
memorySizeKB: memorySizeKB,
|
||||||
|
columns: columns,
|
||||||
|
segments: segments,
|
||||||
|
blocks: blocks,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [Argon2Context] instance from an encoded PHC-compliant string.
|
||||||
|
///
|
||||||
|
/// The encoded string may look like this:
|
||||||
|
/// `$argon2i$v=19$m=16,t=2,p=1$c29tZSBzYWx0$u1eU6mZFG4/OOoTdAtM5SQ`
|
||||||
|
factory Argon2Context.fromEncoded(
|
||||||
|
CryptData data, {
|
||||||
|
List<int>? key,
|
||||||
|
List<int>? personalization,
|
||||||
|
}) {
|
||||||
|
var type = Argon2Type.fromName(data.id);
|
||||||
|
var version = Argon2Version.fromValue(
|
||||||
|
int.tryParse(data.version ?? '0') ?? 0,
|
||||||
|
);
|
||||||
|
if (data.params == null) {
|
||||||
|
throw ArgumentError('No paramters');
|
||||||
|
}
|
||||||
|
var m = data.params!['m'];
|
||||||
|
if (m == null) {
|
||||||
|
throw ArgumentError('Missing parameter: m');
|
||||||
|
}
|
||||||
|
var t = data.params!['t'];
|
||||||
|
if (t == null) {
|
||||||
|
throw ArgumentError('Missing parameter: t');
|
||||||
|
}
|
||||||
|
var p = data.params!['p'];
|
||||||
|
if (p == null) {
|
||||||
|
throw ArgumentError('Missing parameter: p');
|
||||||
|
}
|
||||||
|
return Argon2Context(
|
||||||
|
type: type,
|
||||||
|
version: version,
|
||||||
|
iterations: int.parse(t),
|
||||||
|
parallelism: int.parse(p),
|
||||||
|
memorySizeKB: int.parse(m),
|
||||||
|
salt: data.saltBytes(),
|
||||||
|
hashLength: data.hashBytes()?.lengthInBytes,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the PHC-compliant string for this [Argon2HashDigest]
|
||||||
|
String toEncoded(Uint8List hashBytes) {
|
||||||
|
return toCrypt(
|
||||||
|
CryptDataBuilder(type.name)
|
||||||
|
.version('${version.value}')
|
||||||
|
.param('m', memorySizeKB)
|
||||||
|
.param('t', passes)
|
||||||
|
.param('p', lanes)
|
||||||
|
.saltBytes(salt)
|
||||||
|
.hashBytes(hashBytes)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
110
hashlib/lib/src/algorithms/argon2/security.dart
Normal file
110
hashlib/lib/src/algorithms/argon2/security.dart
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'argon2.dart';
|
||||||
|
|
||||||
|
/// This contains some recommended values of memory, iteration and parallelism
|
||||||
|
/// values for [Argon2] algorithm.
|
||||||
|
///
|
||||||
|
/// It is best to try out different combinations of these values to achieve the
|
||||||
|
/// desired runtime on a target machine.
|
||||||
|
class Argon2Security {
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// The amount of memory to use in KB. The more the better, but slower.
|
||||||
|
final int m;
|
||||||
|
|
||||||
|
/// Number of threads or lanes to use. The more the better, but slower.
|
||||||
|
final int p;
|
||||||
|
|
||||||
|
/// Number of iterations. The more the better, but slower.
|
||||||
|
final int t;
|
||||||
|
|
||||||
|
/// The type of the algorithm
|
||||||
|
final Argon2Type type;
|
||||||
|
|
||||||
|
/// The version of the algorithm
|
||||||
|
final Argon2Version version;
|
||||||
|
|
||||||
|
const Argon2Security(
|
||||||
|
this.name, {
|
||||||
|
required this.m,
|
||||||
|
required this.p,
|
||||||
|
required this.t,
|
||||||
|
this.type = Argon2Type.argon2id,
|
||||||
|
this.version = Argon2Version.v13,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Provides a very low security. Use it for test purposes.
|
||||||
|
///
|
||||||
|
/// It uses 32KB of memory, 2 lanes, and 2 iterations.
|
||||||
|
///
|
||||||
|
/// **WARNING: Not recommended for general use.**
|
||||||
|
static const test = Argon2Security('test', m: 1 << 5, p: 4, t: 3);
|
||||||
|
|
||||||
|
/// Provides low security but faster. Suitable for low-end devices.
|
||||||
|
///
|
||||||
|
/// It uses 1MB of memory, 8 lanes, and 2 iterations.
|
||||||
|
static const little = Argon2Security('little', m: 1 << 10, p: 8, t: 2);
|
||||||
|
|
||||||
|
/// Provides moderate security. Suitable for modern mobile devices.
|
||||||
|
///
|
||||||
|
/// It uses 8MB of memory, 4 lanes, and 3 iterations.
|
||||||
|
/// This is 10x slower than the [little] one.
|
||||||
|
static const moderate = Argon2Security('moderate', m: 1 << 13, p: 4, t: 2);
|
||||||
|
|
||||||
|
/// Provides good security. Second recommended option by [RFC-9106][rfc].
|
||||||
|
///
|
||||||
|
/// It uses 64MB of memory, 4 lanes, and 3 iterations.
|
||||||
|
/// This is 10x slower than the [moderate] one.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc9106.html
|
||||||
|
static const good = Argon2Security('good', m: 1 << 16, p: 4, t: 3);
|
||||||
|
|
||||||
|
/// Provides strong security. First recommended option by [RFC-9106][rfc].
|
||||||
|
///
|
||||||
|
/// It uses 2GB of memory, 4 lanes, and 1 iteration.
|
||||||
|
/// This is 10x slower than the [good] one.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc9106.html
|
||||||
|
static const strong = Argon2Security('strong', m: 1 << 21, p: 4, t: 1);
|
||||||
|
|
||||||
|
/// Provides strong security recommended by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses 46MB of memory, 1 lane, and 1 iteration.
|
||||||
|
///
|
||||||
|
/// **Do not use with Argon2i.**
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp = Argon2Security('owasp1', m: 47104, t: 1, p: 1);
|
||||||
|
|
||||||
|
/// Second recommendation from [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses 19MB of memory, 1 lane, and 2 iterations.
|
||||||
|
///
|
||||||
|
/// **Do not use with Argon2i.**
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp2 = Argon2Security('owasp2', m: 19456, t: 2, p: 1);
|
||||||
|
|
||||||
|
/// Third recommendation from [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses 12MB of memory, 1 lane, and 3 iterations.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp3 = Argon2Security('owasp3', m: 12288, t: 3, p: 1);
|
||||||
|
|
||||||
|
/// Fourth recommendation from [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses 9MB of memory, 1 lane, and 4 iterations.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp4 = Argon2Security('owasp4', m: 9216, t: 4, p: 1);
|
||||||
|
|
||||||
|
/// Second recommendation from [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses 7MB of memory, 1 lane, and 5 iterations.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp5 = Argon2Security('owasp5', m: 7168, t: 5, p: 1);
|
||||||
|
}
|
||||||
488
hashlib/lib/src/algorithms/bcrypt/bcrypt.dart
Normal file
488
hashlib/lib/src/algorithms/bcrypt/bcrypt.dart
Normal file
|
|
@ -0,0 +1,488 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
import 'package:hashlib/src/algorithms/bcrypt/common.dart';
|
||||||
|
import 'package:hashlib/src/algorithms/bcrypt/security.dart';
|
||||||
|
import 'package:hashlib/src/core/kdf_base.dart';
|
||||||
|
|
||||||
|
/// Implementation for the bcrypt key generator
|
||||||
|
class Bcrypt extends KeyDerivatorBase {
|
||||||
|
final BcryptContext _ctx;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'Bcrypt/${_ctx.version.name}';
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength = 23;
|
||||||
|
|
||||||
|
const Bcrypt._(this._ctx);
|
||||||
|
|
||||||
|
/// Creates an [Bcrypt] instance with a sink for MAC generation.
|
||||||
|
factory Bcrypt({
|
||||||
|
required int cost,
|
||||||
|
List<int>? salt,
|
||||||
|
BcryptVersion version = BcryptVersion.$2b,
|
||||||
|
}) {
|
||||||
|
var ctx = BcryptContext(
|
||||||
|
cost: cost,
|
||||||
|
salt: salt,
|
||||||
|
version: version,
|
||||||
|
);
|
||||||
|
return Bcrypt._(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [Bcrypt] instance from [BcryptSecurity] parameter.
|
||||||
|
factory Bcrypt.fromSecurity(
|
||||||
|
BcryptSecurity security, {
|
||||||
|
List<int>? salt,
|
||||||
|
BcryptVersion version = BcryptVersion.$2b,
|
||||||
|
}) {
|
||||||
|
var ctx = BcryptContext(
|
||||||
|
cost: security.nb,
|
||||||
|
salt: salt,
|
||||||
|
version: version,
|
||||||
|
);
|
||||||
|
return Bcrypt._(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [Bcrypt] instance from encoded string.
|
||||||
|
factory Bcrypt.fromEncoded(CryptData data) {
|
||||||
|
var ctx = BcryptContext.fromEncoded(data);
|
||||||
|
return Bcrypt._(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a derived key using the Bcrypt algorithm.
|
||||||
|
@override
|
||||||
|
BcryptHashDigest convert(List<int> password) {
|
||||||
|
int i, j, l, h, t;
|
||||||
|
int s0, s1, s2, s3;
|
||||||
|
int nb = 1 << _ctx.cost;
|
||||||
|
var pass32 = _ctx.makePassword(password);
|
||||||
|
var salt32 = _makeSaltKey(_ctx.salt);
|
||||||
|
|
||||||
|
// Initialize state: P (Subkeys) + S (Substitution boxes)
|
||||||
|
var state = Uint32List.fromList(_sv);
|
||||||
|
var p = Uint32List.view(state.buffer, 0, 18);
|
||||||
|
var s = Uint32List.view(state.buffer, 18 << 2, 1024);
|
||||||
|
|
||||||
|
// Blowfish encrypt routine
|
||||||
|
l = 0;
|
||||||
|
h = 0;
|
||||||
|
void encrypt() {
|
||||||
|
l ^= p[0];
|
||||||
|
h ^= _fsub(s, l) ^ p[1];
|
||||||
|
l ^= _fsub(s, h) ^ p[2];
|
||||||
|
h ^= _fsub(s, l) ^ p[3];
|
||||||
|
l ^= _fsub(s, h) ^ p[4];
|
||||||
|
h ^= _fsub(s, l) ^ p[5];
|
||||||
|
l ^= _fsub(s, h) ^ p[6];
|
||||||
|
h ^= _fsub(s, l) ^ p[7];
|
||||||
|
l ^= _fsub(s, h) ^ p[8];
|
||||||
|
h ^= _fsub(s, l) ^ p[9];
|
||||||
|
l ^= _fsub(s, h) ^ p[10];
|
||||||
|
h ^= _fsub(s, l) ^ p[11];
|
||||||
|
l ^= _fsub(s, h) ^ p[12];
|
||||||
|
h ^= _fsub(s, l) ^ p[13];
|
||||||
|
l ^= _fsub(s, h) ^ p[14];
|
||||||
|
h ^= _fsub(s, l) ^ p[15];
|
||||||
|
l ^= _fsub(s, h) ^ p[16];
|
||||||
|
h ^= p[17];
|
||||||
|
// swap
|
||||||
|
t = l;
|
||||||
|
l = h;
|
||||||
|
h = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key Expansion
|
||||||
|
l = 0;
|
||||||
|
h = 0;
|
||||||
|
s0 = salt32[0];
|
||||||
|
s1 = salt32[1];
|
||||||
|
s2 = salt32[2];
|
||||||
|
s3 = salt32[3];
|
||||||
|
for (i = 0; i < 18; ++i) {
|
||||||
|
p[i] ^= pass32[i];
|
||||||
|
}
|
||||||
|
for (i = 0; i < state.length; i += 2) {
|
||||||
|
// block ^= salt
|
||||||
|
l ^= s0;
|
||||||
|
h ^= s1;
|
||||||
|
// blowfish encrypt
|
||||||
|
encrypt();
|
||||||
|
// set state
|
||||||
|
state[i] = l;
|
||||||
|
state[i + 1] = h;
|
||||||
|
// s0 <-> s2
|
||||||
|
t = s0;
|
||||||
|
s0 = s2;
|
||||||
|
s2 = t;
|
||||||
|
// s1 <-> s3
|
||||||
|
t = s1;
|
||||||
|
s1 = s3;
|
||||||
|
s3 = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bcrypt Rounds
|
||||||
|
for (j = 0; j < nb; ++j) {
|
||||||
|
// mix with password
|
||||||
|
l = 0;
|
||||||
|
h = 0;
|
||||||
|
for (i = 0; i < 18; ++i) {
|
||||||
|
p[i] ^= pass32[i];
|
||||||
|
}
|
||||||
|
for (i = 0; i < state.length;) {
|
||||||
|
encrypt();
|
||||||
|
state[i++] = l;
|
||||||
|
state[i++] = h;
|
||||||
|
}
|
||||||
|
// mix with salt
|
||||||
|
l = 0;
|
||||||
|
h = 0;
|
||||||
|
for (i = 0; i < 18; ++i) {
|
||||||
|
p[i] ^= salt32[i];
|
||||||
|
}
|
||||||
|
for (i = 0; i < state.length;) {
|
||||||
|
encrypt();
|
||||||
|
state[i++] = l;
|
||||||
|
state[i++] = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt IV
|
||||||
|
var result = Uint32List.fromList(_iv);
|
||||||
|
for (i = 0; i < 64; i++) {
|
||||||
|
l = result[0];
|
||||||
|
h = result[1];
|
||||||
|
encrypt();
|
||||||
|
result[0] = l;
|
||||||
|
result[1] = h;
|
||||||
|
|
||||||
|
l = result[2];
|
||||||
|
h = result[3];
|
||||||
|
encrypt();
|
||||||
|
result[2] = l;
|
||||||
|
result[3] = h;
|
||||||
|
|
||||||
|
l = result[4];
|
||||||
|
h = result[5];
|
||||||
|
encrypt();
|
||||||
|
result[4] = l;
|
||||||
|
result[5] = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform to bytes
|
||||||
|
for (i = 0; i < result.length; ++i) {
|
||||||
|
result[i] = _swap32(result[i]);
|
||||||
|
}
|
||||||
|
var output = Uint8List.view(result.buffer, 0, derivedKeyLength);
|
||||||
|
return BcryptHashDigest(_ctx, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint32List _makeSaltKey(Uint8List salt) {
|
||||||
|
var salt32 = Uint32List.view(salt.buffer);
|
||||||
|
var dest = Uint32List(18);
|
||||||
|
dest[0] = _swap32(salt32[0]);
|
||||||
|
dest[1] = _swap32(salt32[1]);
|
||||||
|
dest[2] = _swap32(salt32[2]);
|
||||||
|
dest[3] = _swap32(salt32[3]);
|
||||||
|
for (int i = 4, j = 0; i < 18; i++, j++) {
|
||||||
|
dest[i] = dest[j];
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _swap32(int x) =>
|
||||||
|
((x << 24) & 0xff000000) |
|
||||||
|
((x << 8) & 0x00ff0000) |
|
||||||
|
((x >>> 8) & 0x0000ff00) |
|
||||||
|
((x >>> 24) & 0x000000ff);
|
||||||
|
|
||||||
|
/// Feistel substitution
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _fsub(Uint32List s, int l) =>
|
||||||
|
((s[(l >>> 24) & 0xff] + //
|
||||||
|
s[0x100 | ((l >>> 16) & 0xff)]) ^
|
||||||
|
s[0x200 | ((l >>> 8) & 0xff)]) +
|
||||||
|
s[0x300 | (l & 0xff)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hex form of text: 'OrpheanBeholderScryDoubt'
|
||||||
|
const List<int> _iv = [
|
||||||
|
0x4f727068,
|
||||||
|
0x65616e42,
|
||||||
|
0x65686f6c,
|
||||||
|
0x64657253,
|
||||||
|
0x63727944,
|
||||||
|
0x6f756274,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Fractional part of PI
|
||||||
|
const List<int> _sv = [
|
||||||
|
// first 18 bytes
|
||||||
|
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||||
|
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||||
|
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||||
|
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||||
|
0x9216d5d9, 0x8979fb1b,
|
||||||
|
// next 1024 bytes
|
||||||
|
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
|
||||||
|
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
|
||||||
|
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||||
|
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
|
||||||
|
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
|
||||||
|
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||||
|
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
|
||||||
|
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
|
||||||
|
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||||
|
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
||||||
|
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
|
||||||
|
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||||
|
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
|
||||||
|
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
|
||||||
|
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||||
|
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
|
||||||
|
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
|
||||||
|
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||||
|
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
|
||||||
|
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
|
||||||
|
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||||
|
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
|
||||||
|
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
|
||||||
|
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||||
|
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
|
||||||
|
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
|
||||||
|
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||||
|
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
|
||||||
|
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
|
||||||
|
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||||
|
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
|
||||||
|
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
|
||||||
|
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||||
|
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
|
||||||
|
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
|
||||||
|
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||||
|
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
|
||||||
|
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
|
||||||
|
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||||
|
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
|
||||||
|
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
|
||||||
|
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||||
|
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
|
||||||
|
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
|
||||||
|
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||||
|
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
|
||||||
|
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
|
||||||
|
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||||
|
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
|
||||||
|
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
|
||||||
|
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||||
|
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
|
||||||
|
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
|
||||||
|
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||||
|
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
|
||||||
|
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
|
||||||
|
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||||
|
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
|
||||||
|
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
|
||||||
|
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||||
|
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
|
||||||
|
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
|
||||||
|
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||||
|
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
||||||
|
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
|
||||||
|
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
|
||||||
|
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||||
|
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
|
||||||
|
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
|
||||||
|
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||||
|
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
|
||||||
|
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
|
||||||
|
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||||
|
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
|
||||||
|
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
|
||||||
|
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||||
|
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
|
||||||
|
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
|
||||||
|
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||||
|
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
|
||||||
|
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
|
||||||
|
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||||
|
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
|
||||||
|
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
|
||||||
|
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||||
|
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
|
||||||
|
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
|
||||||
|
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||||
|
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
|
||||||
|
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
|
||||||
|
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||||
|
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
|
||||||
|
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
|
||||||
|
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||||
|
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
|
||||||
|
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
|
||||||
|
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||||
|
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
|
||||||
|
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
|
||||||
|
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||||
|
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
|
||||||
|
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
|
||||||
|
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||||
|
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
|
||||||
|
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
|
||||||
|
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||||
|
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
|
||||||
|
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
|
||||||
|
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||||
|
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
|
||||||
|
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
|
||||||
|
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||||
|
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
|
||||||
|
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
|
||||||
|
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||||
|
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
|
||||||
|
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
|
||||||
|
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||||
|
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
|
||||||
|
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
|
||||||
|
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||||
|
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
|
||||||
|
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
|
||||||
|
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||||
|
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
|
||||||
|
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
|
||||||
|
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||||
|
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
|
||||||
|
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
|
||||||
|
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
|
||||||
|
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||||
|
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
|
||||||
|
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
|
||||||
|
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||||
|
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
|
||||||
|
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
|
||||||
|
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||||
|
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
|
||||||
|
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
|
||||||
|
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||||
|
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
|
||||||
|
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
|
||||||
|
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||||
|
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
|
||||||
|
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
|
||||||
|
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||||
|
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
|
||||||
|
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
|
||||||
|
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||||
|
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
|
||||||
|
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
|
||||||
|
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||||
|
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
|
||||||
|
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
|
||||||
|
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||||
|
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
|
||||||
|
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
|
||||||
|
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||||
|
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
|
||||||
|
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
|
||||||
|
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||||
|
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
|
||||||
|
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
|
||||||
|
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||||
|
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
|
||||||
|
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
|
||||||
|
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||||
|
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
|
||||||
|
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
|
||||||
|
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||||
|
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
|
||||||
|
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
||||||
|
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||||
|
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
|
||||||
|
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
|
||||||
|
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||||
|
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
|
||||||
|
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
|
||||||
|
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||||
|
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
|
||||||
|
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
|
||||||
|
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||||
|
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
|
||||||
|
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
|
||||||
|
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||||
|
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
|
||||||
|
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
|
||||||
|
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||||
|
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
|
||||||
|
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
||||||
|
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||||
|
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
|
||||||
|
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
|
||||||
|
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
|
||||||
|
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||||
|
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
|
||||||
|
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
|
||||||
|
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||||
|
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
|
||||||
|
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
|
||||||
|
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||||
|
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
|
||||||
|
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
|
||||||
|
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||||
|
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
|
||||||
|
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
|
||||||
|
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||||
|
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
|
||||||
|
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
|
||||||
|
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||||
|
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
|
||||||
|
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
|
||||||
|
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||||
|
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
|
||||||
|
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
|
||||||
|
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||||
|
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
|
||||||
|
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
|
||||||
|
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||||
|
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
|
||||||
|
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
|
||||||
|
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||||
|
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
|
||||||
|
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
|
||||||
|
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||||
|
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
|
||||||
|
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
|
||||||
|
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||||
|
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
|
||||||
|
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
|
||||||
|
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||||
|
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
|
||||||
|
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
|
||||||
|
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||||
|
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
|
||||||
|
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
|
||||||
|
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||||
|
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
|
||||||
|
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
|
||||||
|
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||||
|
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
|
||||||
|
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
|
||||||
|
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||||
|
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
|
||||||
|
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
|
||||||
|
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||||
|
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
|
||||||
|
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
|
||||||
|
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||||
|
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
|
||||||
|
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
|
||||||
|
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||||
|
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
|
||||||
|
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
|
||||||
|
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||||
|
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
|
||||||
|
];
|
||||||
177
hashlib/lib/src/algorithms/bcrypt/common.dart
Normal file
177
hashlib/lib/src/algorithms/bcrypt/common.dart
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
import 'package:hashlib/src/algorithms/bcrypt/bcrypt.dart';
|
||||||
|
import 'package:hashlib/src/core/hash_digest.dart';
|
||||||
|
import 'package:hashlib/src/random.dart';
|
||||||
|
|
||||||
|
/// The [Bcrypt] algorithm version
|
||||||
|
enum BcryptVersion {
|
||||||
|
/// This is a revised version of original v2 with UTF-8 character support
|
||||||
|
/// and inclusion of the null terminator with the password.
|
||||||
|
$2a._('2a'),
|
||||||
|
|
||||||
|
/// This is the bug-fixed version of OpenBSD implementation of bcrypt, which
|
||||||
|
/// fixes the support for password longer than 255 characters.
|
||||||
|
$2b._('2b'),
|
||||||
|
|
||||||
|
/// This was introduced by PHP `crypt_blowfish` implementation to mark hashes
|
||||||
|
/// generated with a bug in their implementation.
|
||||||
|
$2x._('2x'),
|
||||||
|
|
||||||
|
/// This was introduced by PHP `crypt_blowfish` implementation to mark hashes
|
||||||
|
/// generated after fixing the bug in their implementation.
|
||||||
|
$2y._('2y');
|
||||||
|
|
||||||
|
/// The name of this version
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
const BcryptVersion._(this.name);
|
||||||
|
|
||||||
|
/// Gets the version from string name
|
||||||
|
static BcryptVersion fromName(String name) {
|
||||||
|
switch (name) {
|
||||||
|
case '2a':
|
||||||
|
return BcryptVersion.$2a;
|
||||||
|
case '2b':
|
||||||
|
return BcryptVersion.$2b;
|
||||||
|
case '2x':
|
||||||
|
return BcryptVersion.$2x;
|
||||||
|
case '2y':
|
||||||
|
return BcryptVersion.$2y;
|
||||||
|
default:
|
||||||
|
throw FormatException('Invalid version');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The HashDigest for Bcrypt with [BcryptContext]
|
||||||
|
class BcryptHashDigest extends HashDigest {
|
||||||
|
final BcryptContext ctx;
|
||||||
|
|
||||||
|
const BcryptHashDigest(this.ctx, super.bytes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => encoded();
|
||||||
|
|
||||||
|
/// Gets a PHC-compliant encoded string
|
||||||
|
String encoded() => ctx.toEncoded(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The configuration used by the [Bcrypt] algorithm
|
||||||
|
class BcryptContext {
|
||||||
|
/// The BCrypt version
|
||||||
|
final BcryptVersion version;
|
||||||
|
|
||||||
|
/// Number of rounds in terms of power of 2
|
||||||
|
final int cost;
|
||||||
|
|
||||||
|
/// 16-byte salt
|
||||||
|
final Uint8List salt;
|
||||||
|
|
||||||
|
const BcryptContext._({
|
||||||
|
required this.salt,
|
||||||
|
required this.version,
|
||||||
|
required this.cost,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Creates an [BcryptContext] instance from encoded string.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [version] : The BcryptVersion to use. Default `BcryptVersion.$2b`.
|
||||||
|
/// - [salt] : An uniquely and randomly generated string.
|
||||||
|
/// - [cost] : Number of rounds in terms of power of 2. 0 < [cost] < 31.
|
||||||
|
factory BcryptContext({
|
||||||
|
required int cost,
|
||||||
|
List<int>? salt,
|
||||||
|
BcryptVersion version = BcryptVersion.$2b,
|
||||||
|
}) {
|
||||||
|
// validate parameters
|
||||||
|
if (cost < 0) {
|
||||||
|
throw ArgumentError('The cost must be at least 0');
|
||||||
|
}
|
||||||
|
if (cost > 31) {
|
||||||
|
throw ArgumentError('The cost must be at most 31');
|
||||||
|
}
|
||||||
|
salt ??= randomBytes(16);
|
||||||
|
if (salt.length != 16) {
|
||||||
|
throw ArgumentError('The salt must be exactly 16-bytes');
|
||||||
|
}
|
||||||
|
return BcryptContext._(
|
||||||
|
cost: cost,
|
||||||
|
version: version,
|
||||||
|
salt: salt is Uint8List ? salt : Uint8List.fromList(salt),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [BcryptContext] instance from encoded string.
|
||||||
|
///
|
||||||
|
/// The encoded string may look like this:
|
||||||
|
/// `$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC`
|
||||||
|
factory BcryptContext.fromEncoded(CryptData data) {
|
||||||
|
var version = BcryptVersion.fromName(data.id);
|
||||||
|
var cost = int.tryParse(data.salt ?? '0');
|
||||||
|
if (cost == null) {
|
||||||
|
throw FormatException('Invalid cost');
|
||||||
|
}
|
||||||
|
Uint8List? salt;
|
||||||
|
var hash = data.hash;
|
||||||
|
if (hash != null) {
|
||||||
|
if (hash.length != 22 && hash.length != 53) {
|
||||||
|
throw FormatException('Invalid hash');
|
||||||
|
}
|
||||||
|
salt = fromBase64(
|
||||||
|
hash.substring(0, 22),
|
||||||
|
codec: Base64Codec.bcrypt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return BcryptContext(
|
||||||
|
salt: salt,
|
||||||
|
cost: cost,
|
||||||
|
version: version,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a PHC-compliant encoded string
|
||||||
|
String toEncoded([Uint8List? hashBytes]) {
|
||||||
|
var hash = toBase64(salt, codec: Base64Codec.bcrypt);
|
||||||
|
if (hashBytes != null) {
|
||||||
|
hash += toBase64(hashBytes, codec: Base64Codec.bcrypt);
|
||||||
|
}
|
||||||
|
return toCrypt(
|
||||||
|
CryptDataBuilder(version.name)
|
||||||
|
.salt('$cost'.padLeft(2, '0'))
|
||||||
|
.hash(hash)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make the 72-byte long password using version-specific strategy.
|
||||||
|
Uint32List makePassword(List<int> password) {
|
||||||
|
int i, j;
|
||||||
|
var long32 = Uint32List(18);
|
||||||
|
var long = Uint8List.view(long32.buffer);
|
||||||
|
var pass8 = Uint8List.fromList(password);
|
||||||
|
for (i = 0; i < 72 && i < pass8.length; i++) {
|
||||||
|
long[i] = pass8[i];
|
||||||
|
}
|
||||||
|
if (i < 72) {
|
||||||
|
long[i++] = 0;
|
||||||
|
}
|
||||||
|
for (j = 0; i < 72; i++, j++) {
|
||||||
|
long[i] = long[j];
|
||||||
|
}
|
||||||
|
for (i = 0; i < 18; ++i) {
|
||||||
|
j = long32[i];
|
||||||
|
j = ((j << 24) & 0xff000000) |
|
||||||
|
((j << 8) & 0x00ff0000) |
|
||||||
|
((j >>> 8) & 0x0000ff00) |
|
||||||
|
((j >>> 24) & 0x000000ff);
|
||||||
|
long32[i] = j;
|
||||||
|
}
|
||||||
|
return long32;
|
||||||
|
}
|
||||||
|
}
|
||||||
51
hashlib/lib/src/algorithms/bcrypt/security.dart
Normal file
51
hashlib/lib/src/algorithms/bcrypt/security.dart
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'bcrypt.dart';
|
||||||
|
|
||||||
|
/// This contains some recommended parameters for [Bcrypt] algorithm.
|
||||||
|
class BcryptSecurity {
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// The number of rounds in terms of power of 2.
|
||||||
|
final int nb;
|
||||||
|
|
||||||
|
const BcryptSecurity(
|
||||||
|
this.name, {
|
||||||
|
required this.nb,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Provides a very low security. Use it for test purposes.
|
||||||
|
///
|
||||||
|
/// It uses 2^1 = 2 round for encryption.
|
||||||
|
///
|
||||||
|
/// **WARNING: Not recommended for general use.**
|
||||||
|
static const test = BcryptSecurity('test', nb: 1);
|
||||||
|
|
||||||
|
/// Provides low security but faster. Suitable for low-end devices.
|
||||||
|
///
|
||||||
|
/// It uses 2^5 = 32 rounds for encryption.
|
||||||
|
static const little = BcryptSecurity('little', nb: 5);
|
||||||
|
|
||||||
|
/// Provides moderate security. Suitable for modern mobile devices.
|
||||||
|
///
|
||||||
|
/// It uses 2^8 = 256 rounds for encryption.
|
||||||
|
static const moderate = BcryptSecurity('moderate', nb: 8);
|
||||||
|
|
||||||
|
/// Provides good security.
|
||||||
|
///
|
||||||
|
/// It uses 2^12 = 4096 rounds for encryption.
|
||||||
|
static const good = BcryptSecurity('good', nb: 12);
|
||||||
|
|
||||||
|
/// Provides strong security.
|
||||||
|
///
|
||||||
|
/// It uses 2^15 = 32768 rounds for encryption.
|
||||||
|
static const strong = BcryptSecurity('strong', nb: 15);
|
||||||
|
|
||||||
|
/// Provides strong security recommended by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses 2^10 = 1024 rounds for encryption.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp = BcryptSecurity('owasp', nb: 10);
|
||||||
|
}
|
||||||
4
hashlib/lib/src/algorithms/blake2/blake2b.dart
Normal file
4
hashlib/lib/src/algorithms/blake2/blake2b.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
export 'blake2b_64bit.dart' if (dart.library.js) 'blake2b_32bit.dart';
|
||||||
354
hashlib/lib/src/algorithms/blake2/blake2b_32bit.dart
Normal file
354
hashlib/lib/src/algorithms/blake2/blake2b_32bit.dart
Normal file
|
|
@ -0,0 +1,354 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
| BLAKE2b |
|
||||||
|
--------------+------------------+
|
||||||
|
Bits in word | w = 64 |
|
||||||
|
Rounds in F | r = 12 |
|
||||||
|
Block bytes | bb = 128 |
|
||||||
|
Hash bytes | 1 <= nn <= 64 |
|
||||||
|
Key bytes | 0 <= kk <= 64 |
|
||||||
|
Input bytes | 0 <= ll < 2**128 |
|
||||||
|
--------------+------------------+
|
||||||
|
G Rotation | (R1, R2, R3, R4) |
|
||||||
|
constants = | (32, 24, 16, 63) |
|
||||||
|
--------------+------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const int _r1 = 32;
|
||||||
|
const int _r2 = 24;
|
||||||
|
const int _r3 = 16;
|
||||||
|
const int _r4 = 63;
|
||||||
|
|
||||||
|
const _seed = [
|
||||||
|
0xF3BCC908, 0x6A09E667, //
|
||||||
|
0x84CAA73B, 0xBB67AE85,
|
||||||
|
0xFE94F82B, 0x3C6EF372,
|
||||||
|
0x5F1D36F1, 0xA54FF53A,
|
||||||
|
0xADE682D1, 0x510E527F,
|
||||||
|
0x2B3E6C1F, 0x9B05688C,
|
||||||
|
0xFB41BD6B, 0x1F83D9AB,
|
||||||
|
0x137E2179, 0x5BE0CD19,
|
||||||
|
];
|
||||||
|
|
||||||
|
// This is used to contruct sigma2 map. Kept for future reference.
|
||||||
|
// const _sigma = [
|
||||||
|
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // round 0
|
||||||
|
// [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], // round 1
|
||||||
|
// [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], // round 2
|
||||||
|
// [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], // round 3
|
||||||
|
// [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], // round 4
|
||||||
|
// [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], // round 5
|
||||||
|
// [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], // round 6
|
||||||
|
// [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], // round 7
|
||||||
|
// [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], // round 8
|
||||||
|
// [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], // round 9
|
||||||
|
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // round 10
|
||||||
|
// [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], // round 11
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// _sigma.map((e) => e.map((e) => e << 1).toList()).toList();
|
||||||
|
const _sigma2 = [
|
||||||
|
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30], // round 0
|
||||||
|
[28, 20, 8, 16, 18, 30, 26, 12, 2, 24, 0, 4, 22, 14, 10, 6], // round 1
|
||||||
|
[22, 16, 24, 0, 10, 4, 30, 26, 20, 28, 6, 12, 14, 2, 18, 8], // round 2
|
||||||
|
[14, 18, 6, 2, 26, 24, 22, 28, 4, 12, 10, 20, 8, 0, 30, 16], // round 3
|
||||||
|
[18, 0, 10, 14, 4, 8, 20, 30, 28, 2, 22, 24, 12, 16, 6, 26], // round 4
|
||||||
|
[4, 24, 12, 20, 0, 22, 16, 6, 8, 26, 14, 10, 30, 28, 2, 18], // round 5
|
||||||
|
[24, 10, 2, 30, 28, 26, 8, 20, 0, 14, 12, 6, 18, 4, 16, 22], // round 6
|
||||||
|
[26, 22, 14, 28, 24, 2, 6, 18, 10, 0, 30, 8, 16, 12, 4, 20], // round 7
|
||||||
|
[12, 30, 28, 18, 22, 6, 0, 16, 24, 4, 26, 14, 2, 8, 20, 10], // round 8
|
||||||
|
[20, 4, 16, 8, 14, 12, 2, 10, 30, 22, 18, 28, 6, 24, 26, 0], // round 9
|
||||||
|
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30], // round 10
|
||||||
|
[28, 20, 8, 16, 18, 30, 26, 12, 2, 24, 0, 4, 22, 14, 10, 6], // round 11
|
||||||
|
];
|
||||||
|
|
||||||
|
const int _w0 = 0;
|
||||||
|
const int _w1 = _w0 + 2;
|
||||||
|
const int _w2 = _w1 + 2;
|
||||||
|
const int _w3 = _w2 + 2;
|
||||||
|
const int _w4 = _w3 + 2;
|
||||||
|
const int _w5 = _w4 + 2;
|
||||||
|
const int _w6 = _w5 + 2;
|
||||||
|
const int _w7 = _w6 + 2;
|
||||||
|
const int _w8 = _w7 + 2;
|
||||||
|
const int _w9 = _w8 + 2;
|
||||||
|
const int _w10 = _w9 + 2;
|
||||||
|
const int _w11 = _w10 + 2;
|
||||||
|
const int _w12 = _w11 + 2;
|
||||||
|
const int _w13 = _w12 + 2;
|
||||||
|
const int _w14 = _w13 + 2;
|
||||||
|
const int _w15 = _w14 + 2;
|
||||||
|
|
||||||
|
/// Implementation is derived from [RFC-7693][rfc] document for
|
||||||
|
/// "The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)".
|
||||||
|
///
|
||||||
|
/// For reference, the official [blake2][blake2] implementation was followed.
|
||||||
|
///
|
||||||
|
/// Note that blake2b uses 64-bit operations.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc7693.html
|
||||||
|
/// [blake2]: https://github.com/BLAKE2/BLAKE2/blob/master/ref/blake2b-ref.c
|
||||||
|
class Blake2bHash extends BlockHashSink implements MACSinkBase {
|
||||||
|
final List<int>? key;
|
||||||
|
late int _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7;
|
||||||
|
late int _s8, _s9, _s10, _s11, _s12, _s13, _s14, _s15;
|
||||||
|
final Uint32List _var = Uint32List(_w15 + 2);
|
||||||
|
final Uint32List state = Uint32List(_seed.length);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength;
|
||||||
|
|
||||||
|
/// For internal use only.
|
||||||
|
Blake2bHash(
|
||||||
|
int digestSize, {
|
||||||
|
this.key,
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? aad,
|
||||||
|
}) : hashLength = digestSize,
|
||||||
|
derivedKeyLength = digestSize,
|
||||||
|
super(1024 >>> 3) {
|
||||||
|
if (digestSize < 1 || digestSize > 64) {
|
||||||
|
throw ArgumentError('The digest size must be between 1 and 64');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter block from the seed
|
||||||
|
_s0 = _seed[0] ^ 0x01010000 ^ hashLength;
|
||||||
|
_s1 = _seed[1];
|
||||||
|
_s2 = _seed[2];
|
||||||
|
_s3 = _seed[3];
|
||||||
|
_s4 = _seed[4];
|
||||||
|
_s5 = _seed[5];
|
||||||
|
_s6 = _seed[6];
|
||||||
|
_s7 = _seed[7];
|
||||||
|
_s8 = _seed[8];
|
||||||
|
_s9 = _seed[9];
|
||||||
|
_s10 = _seed[10];
|
||||||
|
_s11 = _seed[11];
|
||||||
|
_s12 = _seed[12];
|
||||||
|
_s13 = _seed[13];
|
||||||
|
_s14 = _seed[14];
|
||||||
|
_s15 = _seed[15];
|
||||||
|
|
||||||
|
if (key != null && key!.isNotEmpty) {
|
||||||
|
if (key!.length > 64) {
|
||||||
|
throw ArgumentError('The key should not be greater than 64 bytes');
|
||||||
|
}
|
||||||
|
// Add key length to parameter
|
||||||
|
_s0 ^= key!.length << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (salt != null && salt.isNotEmpty) {
|
||||||
|
if (salt.length != 16) {
|
||||||
|
throw ArgumentError('The valid length of salt is 16 bytes');
|
||||||
|
}
|
||||||
|
for (int i = 0, p = 0; i < 4; i++, p += 8) {
|
||||||
|
_s8 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 4, p = 0; i < 8; i++, p += 8) {
|
||||||
|
_s9 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 8, p = 0; i < 12; i++, p += 8) {
|
||||||
|
_s10 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 12, p = 0; i < 16; i++, p += 8) {
|
||||||
|
_s11 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aad != null && aad.isNotEmpty) {
|
||||||
|
if (aad.length != 16) {
|
||||||
|
throw ArgumentError('The valid length of personalization is 16 bytes');
|
||||||
|
}
|
||||||
|
for (int i = 0, p = 0; i < 4; i++, p += 8) {
|
||||||
|
_s12 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 4, p = 0; i < 8; i++, p += 8) {
|
||||||
|
_s13 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 8, p = 0; i < 12; i++, p += 8) {
|
||||||
|
_s14 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 12, p = 0; i < 16; i++, p += 8) {
|
||||||
|
_s15 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
super.reset();
|
||||||
|
state[0] = _s0;
|
||||||
|
state[1] = _s1;
|
||||||
|
state[2] = _s2;
|
||||||
|
state[3] = _s3;
|
||||||
|
state[4] = _s4;
|
||||||
|
state[5] = _s5;
|
||||||
|
state[6] = _s6;
|
||||||
|
state[7] = _s7;
|
||||||
|
state[8] = _s8;
|
||||||
|
state[9] = _s9;
|
||||||
|
state[10] = _s10;
|
||||||
|
state[11] = _s11;
|
||||||
|
state[12] = _s12;
|
||||||
|
state[13] = _s13;
|
||||||
|
state[14] = _s14;
|
||||||
|
state[15] = _s15;
|
||||||
|
// If the key is present, the first block is the key padded with zeroes
|
||||||
|
if (key != null) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < key!.length; ++i) {
|
||||||
|
buffer[i] = key![i];
|
||||||
|
}
|
||||||
|
for (; i < blockLength; ++i) {
|
||||||
|
buffer[i] = 0;
|
||||||
|
}
|
||||||
|
messageLength = pos = blockLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++, pos++, messageLength++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `v[k] = (v[i] << (64 - n)) | (v[i] >>> n)`
|
||||||
|
static void _rotr(int n, List<int> v, int i, int k) {
|
||||||
|
var a = v[i + 1];
|
||||||
|
var b = v[i];
|
||||||
|
if (n == 32) {
|
||||||
|
v[k + 1] = b;
|
||||||
|
v[k] = a;
|
||||||
|
} else if (n < 32) {
|
||||||
|
v[k + 1] = (b << (32 - n)) | (a >>> n);
|
||||||
|
v[k] = (a << (32 - n)) | (b >>> n);
|
||||||
|
} else {
|
||||||
|
v[k + 1] = (a << (64 - n)) | (b >>> (n - 32));
|
||||||
|
v[k] = (b << (64 - n)) | (a >>> (n - 32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `v[k] = v[i] ^ v[j]`
|
||||||
|
static void _xor(List<int> v, int i, int j, int k) {
|
||||||
|
v[k] = v[i] ^ v[j];
|
||||||
|
v[k + 1] = v[i + 1] ^ v[j + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `v[k] = v[i] + v[j]`
|
||||||
|
static void _add2(List<int> v, int i, int j, int k) {
|
||||||
|
var t = v[i] + v[j];
|
||||||
|
v[k] = t;
|
||||||
|
v[k + 1] = (v[i + 1] + v[j + 1]) + (v[k] < t ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `v[k] = v[i] + v[j] + n[x]`
|
||||||
|
static void _add3(List<int> v, int i, int j, List<int> n, int x, int k) {
|
||||||
|
var t = v[i] + v[j];
|
||||||
|
v[k] = t;
|
||||||
|
v[k + 1] = v[i + 1] + v[j + 1] + (v[k] < t ? 1 : 0);
|
||||||
|
t = v[k] + n[x];
|
||||||
|
v[k] += n[x];
|
||||||
|
v[k + 1] += n[x + 1] + (v[k] < t ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The G function for mixing
|
||||||
|
static void _mix(
|
||||||
|
List<int> v,
|
||||||
|
int a,
|
||||||
|
int b,
|
||||||
|
int c,
|
||||||
|
int d,
|
||||||
|
List<int> m,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
) {
|
||||||
|
// v[a] = (v[a] + v[b] + x);
|
||||||
|
_add3(v, a, b, m, x, a);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], _r1);
|
||||||
|
_xor(v, d, a, d);
|
||||||
|
_rotr(_r1, v, d, d);
|
||||||
|
// v[c] = (v[c] + v[d]);
|
||||||
|
_add2(v, c, d, c);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], _r2);
|
||||||
|
_xor(v, b, c, b);
|
||||||
|
_rotr(_r2, v, b, b);
|
||||||
|
// v[a] = (v[a] + v[b] + y);
|
||||||
|
_add3(v, a, b, m, y, a);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], _r3);
|
||||||
|
_xor(v, d, a, d);
|
||||||
|
_rotr(_r3, v, d, d);
|
||||||
|
// v[c] = (v[c] + v[d]);
|
||||||
|
_add2(v, c, d, c);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], _r4);
|
||||||
|
_xor(v, b, c, b);
|
||||||
|
_rotr(_r4, v, b, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
// Copy state and seed
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
_var[i] = state[i];
|
||||||
|
_var[_w8 + i] = _seed[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
_var[_w12] ^= messageLength & _mask32;
|
||||||
|
_var[_w12 + 1] ^= (messageLength >>> 32) & _mask32;
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
_var[_w14] ^= _mask32;
|
||||||
|
_var[_w14 + 1] ^= _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cryptographic mixing
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
var s = _sigma2[i];
|
||||||
|
_mix(_var, _w0, _w4, _w8, _w12, sbuffer, s[0], s[1]);
|
||||||
|
_mix(_var, _w1, _w5, _w9, _w13, sbuffer, s[2], s[3]);
|
||||||
|
_mix(_var, _w2, _w6, _w10, _w14, sbuffer, s[4], s[5]);
|
||||||
|
_mix(_var, _w3, _w7, _w11, _w15, sbuffer, s[6], s[7]);
|
||||||
|
_mix(_var, _w0, _w5, _w10, _w15, sbuffer, s[8], s[9]);
|
||||||
|
_mix(_var, _w1, _w6, _w11, _w12, sbuffer, s[10], s[11]);
|
||||||
|
_mix(_var, _w2, _w7, _w8, _w13, sbuffer, s[12], s[13]);
|
||||||
|
_mix(_var, _w3, _w4, _w9, _w14, sbuffer, s[14], s[15]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build new state
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
state[i] ^= _var[i] ^ _var[_w8 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < blockLength; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update(buffer, 0, true);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
323
hashlib/lib/src/algorithms/blake2/blake2b_64bit.dart
Normal file
323
hashlib/lib/src/algorithms/blake2/blake2b_64bit.dart
Normal file
|
|
@ -0,0 +1,323 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
| BLAKE2b |
|
||||||
|
--------------+------------------+
|
||||||
|
Bits in word | w = 64 |
|
||||||
|
Rounds in F | r = 12 |
|
||||||
|
Block bytes | bb = 128 |
|
||||||
|
Hash bytes | 1 <= nn <= 64 |
|
||||||
|
Key bytes | 0 <= kk <= 64 |
|
||||||
|
Input bytes | 0 <= ll < 2**128 |
|
||||||
|
--------------+------------------+
|
||||||
|
G Rotation | (R1, R2, R3, R4) |
|
||||||
|
constants = | (32, 24, 16, 63) |
|
||||||
|
--------------+------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
const int _r1 = 32;
|
||||||
|
const int _r2 = 24;
|
||||||
|
const int _r3 = 16;
|
||||||
|
const int _r4 = 63;
|
||||||
|
|
||||||
|
const _seed = [
|
||||||
|
0x6A09E667F3BCC908,
|
||||||
|
0xBB67AE8584CAA73B,
|
||||||
|
0x3C6EF372FE94F82B,
|
||||||
|
0xA54FF53A5F1D36F1,
|
||||||
|
0x510E527FADE682D1,
|
||||||
|
0x9B05688C2B3E6C1F,
|
||||||
|
0x1F83D9ABFB41BD6B,
|
||||||
|
0x5BE0CD19137E2179,
|
||||||
|
];
|
||||||
|
|
||||||
|
const _sigma = [
|
||||||
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // round 0
|
||||||
|
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], // round 1
|
||||||
|
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], // round 2
|
||||||
|
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], // round 3
|
||||||
|
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], // round 4
|
||||||
|
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], // round 5
|
||||||
|
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], // round 6
|
||||||
|
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], // round 7
|
||||||
|
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], // round 8
|
||||||
|
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], // round 9
|
||||||
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // round 10
|
||||||
|
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], // round 11
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Implementation is derived from [RFC-7693][rfc] document for
|
||||||
|
/// "The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)".
|
||||||
|
///
|
||||||
|
/// For reference, the official [blake2][blake2] implementation was followed.
|
||||||
|
///
|
||||||
|
/// Note that blake2b uses 64-bit operations.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc7693.html
|
||||||
|
/// [blake2]: https://github.com/BLAKE2/BLAKE2/blob/master/ref/blake2b-ref.c
|
||||||
|
class Blake2bHash extends BlockHashSink implements MACSinkBase {
|
||||||
|
final List<int>? key;
|
||||||
|
late int _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7;
|
||||||
|
final Uint64List state = Uint64List(_seed.length);
|
||||||
|
late final Uint64List qbuffer = Uint64List.view(buffer.buffer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength;
|
||||||
|
|
||||||
|
/// For internal use only.
|
||||||
|
Blake2bHash(
|
||||||
|
int digestSize, {
|
||||||
|
this.key,
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? aad,
|
||||||
|
}) : hashLength = digestSize,
|
||||||
|
derivedKeyLength = digestSize,
|
||||||
|
super(1024 >>> 3) {
|
||||||
|
if (digestSize < 1 || digestSize > 64) {
|
||||||
|
throw ArgumentError('The digest size must be between 1 and 64');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter block from the seed
|
||||||
|
_s0 = _seed[0] ^ 0x01010000 ^ hashLength;
|
||||||
|
_s1 = _seed[1];
|
||||||
|
_s2 = _seed[2];
|
||||||
|
_s3 = _seed[3];
|
||||||
|
_s4 = _seed[4];
|
||||||
|
_s5 = _seed[5];
|
||||||
|
_s6 = _seed[6];
|
||||||
|
_s7 = _seed[7];
|
||||||
|
|
||||||
|
if (key != null && key!.isNotEmpty) {
|
||||||
|
if (key!.length > 64) {
|
||||||
|
throw ArgumentError('The key should not be greater than 64 bytes');
|
||||||
|
}
|
||||||
|
// Add key length to parameter
|
||||||
|
_s0 ^= key!.length << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (salt != null && salt.isNotEmpty) {
|
||||||
|
if (salt.length != 16) {
|
||||||
|
throw ArgumentError('The valid length of salt is 16 bytes');
|
||||||
|
}
|
||||||
|
for (int i = 0, p = 0; i < 8; i++, p += 8) {
|
||||||
|
_s4 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 8, p = 0; i < 16; i++, p += 8) {
|
||||||
|
_s5 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aad != null && aad.isNotEmpty) {
|
||||||
|
if (aad.length != 16) {
|
||||||
|
throw ArgumentError('The valid length of personalization is 16 bytes');
|
||||||
|
}
|
||||||
|
for (int i = 0, p = 0; i < 8; i++, p += 8) {
|
||||||
|
_s6 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 8, p = 0; i < 16; i++, p += 8) {
|
||||||
|
_s7 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
super.reset();
|
||||||
|
state[0] = _s0;
|
||||||
|
state[1] = _s1;
|
||||||
|
state[2] = _s2;
|
||||||
|
state[3] = _s3;
|
||||||
|
state[4] = _s4;
|
||||||
|
state[5] = _s5;
|
||||||
|
state[6] = _s6;
|
||||||
|
state[7] = _s7;
|
||||||
|
// If the key is present, the first block is the key padded with zeroes
|
||||||
|
if (key != null) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < key!.length; ++i) {
|
||||||
|
buffer[i] = key![i];
|
||||||
|
}
|
||||||
|
for (; i < blockLength; ++i) {
|
||||||
|
buffer[i] = 0;
|
||||||
|
}
|
||||||
|
messageLength = pos = blockLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++, pos++, messageLength++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates x right by n bits.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotr(int x, int n) => (x >>> n) ^ (x << (64 - n));
|
||||||
|
|
||||||
|
// static void _G(Uint64List v, int a, int b, int c, int d, int x, int y) {
|
||||||
|
// v[a] = (v[a] + v[b] + x);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], _r1);
|
||||||
|
// v[c] = (v[c] + v[d]);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], _r2);
|
||||||
|
// v[a] = (v[a] + v[b] + y);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], _r3);
|
||||||
|
// v[c] = (v[c] + v[d]);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], _r4);
|
||||||
|
// }
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
var m = qbuffer;
|
||||||
|
int w0, w1, w2, w3, w4, w5, w6, w7;
|
||||||
|
int w8, w9, w10, w11, w12, w13, w14, w15;
|
||||||
|
|
||||||
|
// first half from state
|
||||||
|
w0 = state[0];
|
||||||
|
w1 = state[1];
|
||||||
|
w2 = state[2];
|
||||||
|
w3 = state[3];
|
||||||
|
w4 = state[4];
|
||||||
|
w5 = state[5];
|
||||||
|
w6 = state[6];
|
||||||
|
w7 = state[7];
|
||||||
|
|
||||||
|
// second half from IV
|
||||||
|
w8 = _seed[0];
|
||||||
|
w9 = _seed[1];
|
||||||
|
w10 = _seed[2];
|
||||||
|
w11 = _seed[3];
|
||||||
|
w12 = _seed[4] ^ messageLength;
|
||||||
|
w13 = _seed[5];
|
||||||
|
w14 = _seed[6];
|
||||||
|
w15 = _seed[7];
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
w14 = ~w14; // invert bits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cryptographic mixing
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
var s = _sigma[i];
|
||||||
|
|
||||||
|
// _G(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
|
||||||
|
w0 += w4 + m[s[0]];
|
||||||
|
w12 = _rotr(w12 ^ w0, _r1);
|
||||||
|
w8 += w12;
|
||||||
|
w4 = _rotr(w4 ^ w8, _r2);
|
||||||
|
w0 += w4 + m[s[1]];
|
||||||
|
w12 = _rotr(w12 ^ w0, _r3);
|
||||||
|
w8 += w12;
|
||||||
|
w4 = _rotr(w4 ^ w8, _r4);
|
||||||
|
|
||||||
|
// _G(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
|
||||||
|
w1 += w5 + m[s[2]];
|
||||||
|
w13 = _rotr(w13 ^ w1, _r1);
|
||||||
|
w9 += w13;
|
||||||
|
w5 = _rotr(w5 ^ w9, _r2);
|
||||||
|
w1 += w5 + m[s[3]];
|
||||||
|
w13 = _rotr(w13 ^ w1, _r3);
|
||||||
|
w9 += w13;
|
||||||
|
w5 = _rotr(w5 ^ w9, _r4);
|
||||||
|
|
||||||
|
// _G(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
|
||||||
|
w2 += w6 + m[s[4]];
|
||||||
|
w14 = _rotr(w14 ^ w2, _r1);
|
||||||
|
w10 += w14;
|
||||||
|
w6 = _rotr(w6 ^ w10, _r2);
|
||||||
|
w2 += w6 + m[s[5]];
|
||||||
|
w14 = _rotr(w14 ^ w2, _r3);
|
||||||
|
w10 += w14;
|
||||||
|
w6 = _rotr(w6 ^ w10, _r4);
|
||||||
|
|
||||||
|
// _G(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
|
||||||
|
w3 += w7 + m[s[6]];
|
||||||
|
w15 = _rotr(w15 ^ w3, _r1);
|
||||||
|
w11 += w15;
|
||||||
|
w7 = _rotr(w7 ^ w11, _r2);
|
||||||
|
w3 += w7 + m[s[7]];
|
||||||
|
w15 = _rotr(w15 ^ w3, _r3);
|
||||||
|
w11 += w15;
|
||||||
|
w7 = _rotr(w7 ^ w11, _r4);
|
||||||
|
|
||||||
|
// _G(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
|
||||||
|
w0 += w5 + m[s[8]];
|
||||||
|
w15 = _rotr(w15 ^ w0, _r1);
|
||||||
|
w10 += w15;
|
||||||
|
w5 = _rotr(w5 ^ w10, _r2);
|
||||||
|
w0 += w5 + m[s[9]];
|
||||||
|
w15 = _rotr(w15 ^ w0, _r3);
|
||||||
|
w10 += w15;
|
||||||
|
w5 = _rotr(w5 ^ w10, _r4);
|
||||||
|
|
||||||
|
// _G(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
|
||||||
|
w1 += w6 + m[s[10]];
|
||||||
|
w12 = _rotr(w12 ^ w1, _r1);
|
||||||
|
w11 += w12;
|
||||||
|
w6 = _rotr(w6 ^ w11, _r2);
|
||||||
|
w1 += w6 + m[s[11]];
|
||||||
|
w12 = _rotr(w12 ^ w1, _r3);
|
||||||
|
w11 += w12;
|
||||||
|
w6 = _rotr(w6 ^ w11, _r4);
|
||||||
|
|
||||||
|
// _G(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
|
||||||
|
w2 += w7 + m[s[12]];
|
||||||
|
w13 = _rotr(w13 ^ w2, _r1);
|
||||||
|
w8 += w13;
|
||||||
|
w7 = _rotr(w7 ^ w8, _r2);
|
||||||
|
w2 += w7 + m[s[13]];
|
||||||
|
w13 = _rotr(w13 ^ w2, _r3);
|
||||||
|
w8 += w13;
|
||||||
|
w7 = _rotr(w7 ^ w8, _r4);
|
||||||
|
|
||||||
|
// _G(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
|
||||||
|
w3 += w4 + m[s[14]];
|
||||||
|
w14 = _rotr(w14 ^ w3, _r1);
|
||||||
|
w9 += w14;
|
||||||
|
w4 = _rotr(w4 ^ w9, _r2);
|
||||||
|
w3 += w4 + m[s[15]];
|
||||||
|
w14 = _rotr(w14 ^ w3, _r3);
|
||||||
|
w9 += w14;
|
||||||
|
w4 = _rotr(w4 ^ w9, _r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR the two halves for new state
|
||||||
|
state[0] ^= w0 ^ w8;
|
||||||
|
state[1] ^= w1 ^ w9;
|
||||||
|
state[2] ^= w2 ^ w10;
|
||||||
|
state[3] ^= w3 ^ w11;
|
||||||
|
state[4] ^= w4 ^ w12;
|
||||||
|
state[5] ^= w5 ^ w13;
|
||||||
|
state[6] ^= w6 ^ w14;
|
||||||
|
state[7] ^= w7 ^ w15;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < blockLength; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update(buffer, 0, true);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
323
hashlib/lib/src/algorithms/blake2/blake2s.dart
Normal file
323
hashlib/lib/src/algorithms/blake2/blake2s.dart
Normal file
|
|
@ -0,0 +1,323 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
| BLAKE2s |
|
||||||
|
--------------+------------------+
|
||||||
|
Bits in word | w = 32 |
|
||||||
|
Rounds in F | r = 10 |
|
||||||
|
Block bytes | bb = 64 |
|
||||||
|
Hash bytes | 1 <= nn <= 32 |
|
||||||
|
Key bytes | 0 <= kk <= 32 |
|
||||||
|
Input bytes | 0 <= ll < 2**64 |
|
||||||
|
--------------+------------------+
|
||||||
|
G Rotation | (R1, R2, R3, R4) |
|
||||||
|
constants = | (16, 12, 8, 7) |
|
||||||
|
--------------+------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const int _r1 = 16;
|
||||||
|
const int _r2 = 12;
|
||||||
|
const int _r3 = 8;
|
||||||
|
const int _r4 = 7;
|
||||||
|
|
||||||
|
// Same as SHA-256
|
||||||
|
const _seed = [
|
||||||
|
0x6A09E667, // a
|
||||||
|
0xBB67AE85, // b
|
||||||
|
0x3C6EF372, // c
|
||||||
|
0xA54FF53A, // d
|
||||||
|
0x510E527F, // e
|
||||||
|
0x9B05688C, // f
|
||||||
|
0x1F83D9AB, // g
|
||||||
|
0x5BE0CD19, // h
|
||||||
|
];
|
||||||
|
|
||||||
|
const _sigma = [
|
||||||
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // round 0
|
||||||
|
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], // round 1
|
||||||
|
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], // round 2
|
||||||
|
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], // round 3
|
||||||
|
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], // round 4
|
||||||
|
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], // round 5
|
||||||
|
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], // round 6
|
||||||
|
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], // round 7
|
||||||
|
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], // round 8
|
||||||
|
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], // round 9
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Implementation is derived from [RFC-7693][rfc] document for
|
||||||
|
/// "The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)".
|
||||||
|
///
|
||||||
|
/// For reference, the official [blake2][blake2] implementation was followed.
|
||||||
|
///
|
||||||
|
/// Note that blake2s uses only 32-bit operations.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc7693.html
|
||||||
|
/// [blake2]: https://github.com/BLAKE2/BLAKE2/blob/master/ref/blake2b-ref.c
|
||||||
|
class Blake2sHash extends BlockHashSink implements MACSinkBase {
|
||||||
|
final List<int>? key;
|
||||||
|
late int _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7;
|
||||||
|
final Uint32List state = Uint32List(_seed.length);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength;
|
||||||
|
|
||||||
|
/// For internal use only.
|
||||||
|
Blake2sHash(
|
||||||
|
int digestSize, {
|
||||||
|
this.key,
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? aad,
|
||||||
|
}) : hashLength = digestSize,
|
||||||
|
derivedKeyLength = digestSize,
|
||||||
|
super(64) {
|
||||||
|
if (digestSize < 1 || digestSize > 32) {
|
||||||
|
throw ArgumentError('The digest size must be between 1 and 32');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter block from the seed
|
||||||
|
_s0 = _seed[0] ^ 0x01010000 ^ hashLength;
|
||||||
|
_s1 = _seed[1];
|
||||||
|
_s2 = _seed[2];
|
||||||
|
_s3 = _seed[3];
|
||||||
|
_s4 = _seed[4];
|
||||||
|
_s5 = _seed[5];
|
||||||
|
_s6 = _seed[6];
|
||||||
|
_s7 = _seed[7];
|
||||||
|
|
||||||
|
// Add key length to parameter
|
||||||
|
if (key != null && key!.isNotEmpty) {
|
||||||
|
if (key!.length > 32) {
|
||||||
|
throw ArgumentError('The key should not be greater than 32 bytes');
|
||||||
|
}
|
||||||
|
_s0 ^= key!.length << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (salt != null && salt.isNotEmpty) {
|
||||||
|
if (salt.length != 8) {
|
||||||
|
throw ArgumentError('The valid length of salt is 8 bytes');
|
||||||
|
}
|
||||||
|
for (int i = 0, p = 0; i < 4; i++, p += 8) {
|
||||||
|
_s4 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 4, p = 0; i < 8; i++, p += 8) {
|
||||||
|
_s5 ^= (salt[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aad != null && aad.isNotEmpty) {
|
||||||
|
if (aad.length != 8) {
|
||||||
|
throw ArgumentError('The valid length of personalization is 8 bytes');
|
||||||
|
}
|
||||||
|
for (int i = 0, p = 0; i < 4; i++, p += 8) {
|
||||||
|
_s6 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
for (int i = 4, p = 0; i < 8; i++, p += 8) {
|
||||||
|
_s7 ^= (aad[i] & 0xFF) << p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
super.reset();
|
||||||
|
state[0] = _s0;
|
||||||
|
state[1] = _s1;
|
||||||
|
state[2] = _s2;
|
||||||
|
state[3] = _s3;
|
||||||
|
state[4] = _s4;
|
||||||
|
state[5] = _s5;
|
||||||
|
state[6] = _s6;
|
||||||
|
state[7] = _s7;
|
||||||
|
// If the key is present, the first block is the key padded with zeroes
|
||||||
|
if (key != null) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < key!.length; ++i) {
|
||||||
|
buffer[i] = key![i];
|
||||||
|
}
|
||||||
|
for (; i < blockLength; ++i) {
|
||||||
|
buffer[i] = 0;
|
||||||
|
}
|
||||||
|
messageLength = pos = blockLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++, pos++, messageLength++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates x right by n bits.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotr(int x, int n) => ((x & _mask32) >>> n) | ((x << (32 - n)));
|
||||||
|
|
||||||
|
// static void _G(Uint32List v, int a, int b, int c, int d, int x, int y) {
|
||||||
|
// v[a] = (v[a] + v[b] + x);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], _r1);
|
||||||
|
// v[c] = (v[c] + v[d]);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], _r2);
|
||||||
|
// v[a] = (v[a] + v[b] + y);
|
||||||
|
// v[d] = _rotr(v[d] ^ v[a], _r3);
|
||||||
|
// v[c] = (v[c] + v[d]);
|
||||||
|
// v[b] = _rotr(v[b] ^ v[c], _r4);
|
||||||
|
// }
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
var m = sbuffer;
|
||||||
|
int w0, w1, w2, w3, w4, w5, w6, w7;
|
||||||
|
int w8, w9, w10, w11, w12, w13, w14, w15;
|
||||||
|
|
||||||
|
// first half from state
|
||||||
|
w0 = state[0];
|
||||||
|
w1 = state[1];
|
||||||
|
w2 = state[2];
|
||||||
|
w3 = state[3];
|
||||||
|
w4 = state[4];
|
||||||
|
w5 = state[5];
|
||||||
|
w6 = state[6];
|
||||||
|
w7 = state[7];
|
||||||
|
|
||||||
|
// second half from IV
|
||||||
|
w8 = _seed[0];
|
||||||
|
w9 = _seed[1];
|
||||||
|
w10 = _seed[2];
|
||||||
|
w11 = _seed[3];
|
||||||
|
w12 = _seed[4] ^ (messageLength & _mask32);
|
||||||
|
w13 = _seed[5] ^ (messageLength >>> 32);
|
||||||
|
w14 = _seed[6];
|
||||||
|
w15 = _seed[7];
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
w14 ^= _mask32; // invert bits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cryptographic mixing
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
var s = _sigma[i];
|
||||||
|
|
||||||
|
// _G(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
|
||||||
|
w0 += w4 + m[s[0]];
|
||||||
|
w12 = _rotr(w12 ^ w0, _r1);
|
||||||
|
w8 += w12;
|
||||||
|
w4 = _rotr(w4 ^ w8, _r2);
|
||||||
|
w0 += w4 + m[s[1]];
|
||||||
|
w12 = _rotr(w12 ^ w0, _r3);
|
||||||
|
w8 += w12;
|
||||||
|
w4 = _rotr(w4 ^ w8, _r4);
|
||||||
|
|
||||||
|
// _G(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
|
||||||
|
w1 += w5 + m[s[2]];
|
||||||
|
w13 = _rotr(w13 ^ w1, _r1);
|
||||||
|
w9 += w13;
|
||||||
|
w5 = _rotr(w5 ^ w9, _r2);
|
||||||
|
w1 += w5 + m[s[3]];
|
||||||
|
w13 = _rotr(w13 ^ w1, _r3);
|
||||||
|
w9 += w13;
|
||||||
|
w5 = _rotr(w5 ^ w9, _r4);
|
||||||
|
|
||||||
|
// _G(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
|
||||||
|
w2 += w6 + m[s[4]];
|
||||||
|
w14 = _rotr(w14 ^ w2, _r1);
|
||||||
|
w10 += w14;
|
||||||
|
w6 = _rotr(w6 ^ w10, _r2);
|
||||||
|
w2 += w6 + m[s[5]];
|
||||||
|
w14 = _rotr(w14 ^ w2, _r3);
|
||||||
|
w10 += w14;
|
||||||
|
w6 = _rotr(w6 ^ w10, _r4);
|
||||||
|
|
||||||
|
// _G(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
|
||||||
|
w3 += w7 + m[s[6]];
|
||||||
|
w15 = _rotr(w15 ^ w3, _r1);
|
||||||
|
w11 += w15;
|
||||||
|
w7 = _rotr(w7 ^ w11, _r2);
|
||||||
|
w3 += w7 + m[s[7]];
|
||||||
|
w15 = _rotr(w15 ^ w3, _r3);
|
||||||
|
w11 += w15;
|
||||||
|
w7 = _rotr(w7 ^ w11, _r4);
|
||||||
|
|
||||||
|
// _G(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
|
||||||
|
w0 += w5 + m[s[8]];
|
||||||
|
w15 = _rotr(w15 ^ w0, _r1);
|
||||||
|
w10 += w15;
|
||||||
|
w5 = _rotr(w5 ^ w10, _r2);
|
||||||
|
w0 += w5 + m[s[9]];
|
||||||
|
w15 = _rotr(w15 ^ w0, _r3);
|
||||||
|
w10 += w15;
|
||||||
|
w5 = _rotr(w5 ^ w10, _r4);
|
||||||
|
|
||||||
|
// _G(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
|
||||||
|
w1 += w6 + m[s[10]];
|
||||||
|
w12 = _rotr(w12 ^ w1, _r1);
|
||||||
|
w11 += w12;
|
||||||
|
w6 = _rotr(w6 ^ w11, _r2);
|
||||||
|
w1 += w6 + m[s[11]];
|
||||||
|
w12 = _rotr(w12 ^ w1, _r3);
|
||||||
|
w11 += w12;
|
||||||
|
w6 = _rotr(w6 ^ w11, _r4);
|
||||||
|
|
||||||
|
// _G(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
|
||||||
|
w2 += w7 + m[s[12]];
|
||||||
|
w13 = _rotr(w13 ^ w2, _r1);
|
||||||
|
w8 += w13;
|
||||||
|
w7 = _rotr(w7 ^ w8, _r2);
|
||||||
|
w2 += w7 + m[s[13]];
|
||||||
|
w13 = _rotr(w13 ^ w2, _r3);
|
||||||
|
w8 += w13;
|
||||||
|
w7 = _rotr(w7 ^ w8, _r4);
|
||||||
|
|
||||||
|
// _G(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
|
||||||
|
w3 += w4 + m[s[14]];
|
||||||
|
w14 = _rotr(w14 ^ w3, _r1);
|
||||||
|
w9 += w14;
|
||||||
|
w4 = _rotr(w4 ^ w9, _r2);
|
||||||
|
w3 += w4 + m[s[15]];
|
||||||
|
w14 = _rotr(w14 ^ w3, _r3);
|
||||||
|
w9 += w14;
|
||||||
|
w4 = _rotr(w4 ^ w9, _r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR the two halves for new state
|
||||||
|
state[0] ^= w0 ^ w8;
|
||||||
|
state[1] ^= w1 ^ w9;
|
||||||
|
state[2] ^= w2 ^ w10;
|
||||||
|
state[3] ^= w3 ^ w11;
|
||||||
|
state[4] ^= w4 ^ w12;
|
||||||
|
state[5] ^= w5 ^ w13;
|
||||||
|
state[6] ^= w6 ^ w14;
|
||||||
|
state[7] ^= w7 ^ w15;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < blockLength; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update(buffer, 0, true);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
246
hashlib/lib/src/algorithms/crc/crc16.dart
Normal file
246
hashlib/lib/src/algorithms/crc/crc16.dart
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
const int _mask16 = 0xFFFF;
|
||||||
|
|
||||||
|
/// Predefined polynomials for CRC-16.
|
||||||
|
///
|
||||||
|
/// The predefined polynomials comes from various sources:
|
||||||
|
/// - https://crccalc.com/
|
||||||
|
/// - https://docs.rs/crate/crc16
|
||||||
|
/// - https://crcmod.sourceforge.net/crcmod.predefined.html
|
||||||
|
/// - https://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
||||||
|
class CRC16Params {
|
||||||
|
// 0x8005 group
|
||||||
|
static const arc =
|
||||||
|
CRC16Params._('ARC', 0x8005, 0x0000, true, 0x0000); // 0xBB3D
|
||||||
|
static const cms =
|
||||||
|
CRC16Params._('CMS', 0x8005, 0xFFFF, false, 0x0000); // 0xAEE7
|
||||||
|
static const dds110 =
|
||||||
|
CRC16Params._('DDS-110', 0x8005, 0x800D, false, 0x0000); // 0x9ECF
|
||||||
|
static const maximDow =
|
||||||
|
CRC16Params._('MAXIM-DOW', 0x8005, 0x0000, true, 0xFFFF); // 0x44C2
|
||||||
|
static const modbus =
|
||||||
|
CRC16Params._('MODBUS', 0x8005, 0xFFFF, true, 0x0000); // 0x4B37
|
||||||
|
static const umts =
|
||||||
|
CRC16Params._('UMTS', 0x8005, 0x0000, false, 0x0000); // 0xFEE8
|
||||||
|
static const usb =
|
||||||
|
CRC16Params._('USB', 0x8005, 0xFFFF, true, 0xFFFF); // 0xB4C8
|
||||||
|
|
||||||
|
// 0x1021 group
|
||||||
|
static const genibus =
|
||||||
|
CRC16Params._('GENIBUS', 0x1021, 0xFFFF, false, 0xFFFF); // 0xD64E
|
||||||
|
static const gsm =
|
||||||
|
CRC16Params._('GSM', 0x1021, 0x0000, false, 0xFFFF); // 0xCE3C
|
||||||
|
static const ibm3740 =
|
||||||
|
CRC16Params._('IBM-3740', 0x1021, 0xFFFF, false, 0x0000); // 0x29B1
|
||||||
|
static const ccittFalse =
|
||||||
|
CRC16Params._('CCITT-FALSE', 0x1021, 0xFFFF, false, 0x0000); // 0x29B1
|
||||||
|
static const ibmSdlc =
|
||||||
|
CRC16Params._('IBM-SDLC', 0x1021, 0xFFFF, true, 0xFFFF); // 0x906E
|
||||||
|
static const x25 =
|
||||||
|
CRC16Params._('X-25', 0x1021, 0xFFFF, true, 0xFFFF); // 0x906E
|
||||||
|
static const iso =
|
||||||
|
CRC16Params._('ISO-IEC-14443-3-A', 0x1021, 0x6363, true); // 0xBF05
|
||||||
|
static const kermit =
|
||||||
|
CRC16Params._('KERMIT', 0x1021, 0x0000, true, 0x0000); // 0x2189
|
||||||
|
static const mcrf4xx =
|
||||||
|
CRC16Params._('MCRF4XX', 0x1021, 0xFFFF, true, 0x0000); // 0x6F91
|
||||||
|
static const riello =
|
||||||
|
CRC16Params._('RIELLO', 0x1021, 0x554D, true, 0x0000); // 0x63D0
|
||||||
|
static const augCcitt =
|
||||||
|
CRC16Params._('AUG-CCITT', 0x1021, 0x1D0F, false, 0x0000); // 0xE5CC
|
||||||
|
static const spiFujitsu =
|
||||||
|
CRC16Params._('SPI-FUJITSU', 0x1021, 0x1D0F, false, 0x0000); // 0xE5CC
|
||||||
|
static const tms37157 =
|
||||||
|
CRC16Params._('TMS37157', 0x1021, 0x3791, true, 0x0000); // 0x26B1
|
||||||
|
static const xmodem =
|
||||||
|
CRC16Params._('XMODEM', 0x1021, 0x0000, false, 0x0000); // 0x31C3
|
||||||
|
|
||||||
|
// Others
|
||||||
|
static const cdma2000 =
|
||||||
|
CRC16Params._('CDMA2000', 0xC867, 0xFFFF, false, 0x0000); // 0x4C06
|
||||||
|
static const dectR =
|
||||||
|
CRC16Params._('DECT-R', 0x0589, 0x0000, false, 0x0001); // 0x007E
|
||||||
|
static const dectX =
|
||||||
|
CRC16Params._('DECT-X', 0x0589, 0x0000, false, 0x0000); // 0x007F
|
||||||
|
static const dnp =
|
||||||
|
CRC16Params._('DNP', 0x3D65, 0x0000, true, 0xFFFF); // 0xEA82
|
||||||
|
static const en13757 =
|
||||||
|
CRC16Params._('EN-13757', 0x3D65, 0x0000, false, 0xFFFF); // 0xC2B7
|
||||||
|
static const lj1200 =
|
||||||
|
CRC16Params._('LJ1200', 0x6F63, 0x0000, false, 0x0000); // 0xBDF4
|
||||||
|
static const nrsc5 =
|
||||||
|
CRC16Params._('NRSC-5', 0x080B, 0xFFFF, true, 0x0000); // 0xA066
|
||||||
|
static const m17 =
|
||||||
|
CRC16Params._('M17', 0x5935, 0xFFFF, false, 0x0000); // 0x772B
|
||||||
|
static const opensafetyA =
|
||||||
|
CRC16Params._('OPENSAFETY-A', 0x5935, 0x0000, false, 0x0000); // 0x5D38
|
||||||
|
static const opensafetyB =
|
||||||
|
CRC16Params._('OPENSAFETY-B', 0x755B, 0x0000, false, 0x0000); // 0x20FE
|
||||||
|
static const profibus =
|
||||||
|
CRC16Params._('PROFIBUS', 0x1DCF, 0xFFFF, false, 0xFFFF); // 0xA819
|
||||||
|
static const t10Dif =
|
||||||
|
CRC16Params._('T10-DIF', 0x8BB7, 0x0000, false, 0x0000); // 0xD0DB
|
||||||
|
static const teledisk =
|
||||||
|
CRC16Params._('TELEDISK', 0xA097, 0x0000, false, 0x0000); // 0x0FB3
|
||||||
|
|
||||||
|
// From Wikipedia
|
||||||
|
static const arinc =
|
||||||
|
CRC16Params._("ARINC", 0xA02B, 0x0000, false, 0x0000); // 0xEBA4
|
||||||
|
|
||||||
|
// aliases
|
||||||
|
static const ibm = arc;
|
||||||
|
static const ansi = arc;
|
||||||
|
static const lha = arc;
|
||||||
|
static const arinc453 = arc;
|
||||||
|
static const ccitt = kermit;
|
||||||
|
static const bluetooth = kermit;
|
||||||
|
static const buypass = umts;
|
||||||
|
static const autosar = genibus;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Implementation
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Polynomial name
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Polynomial value
|
||||||
|
final int poly;
|
||||||
|
|
||||||
|
/// Initial CRC
|
||||||
|
final int seed;
|
||||||
|
|
||||||
|
/// Output XOR value
|
||||||
|
final int xorOut;
|
||||||
|
|
||||||
|
/// To use the reverse of the polynomial
|
||||||
|
final bool reversed;
|
||||||
|
|
||||||
|
const CRC16Params._(
|
||||||
|
this.name,
|
||||||
|
this.poly,
|
||||||
|
this.seed,
|
||||||
|
this.reversed, [
|
||||||
|
this.xorOut = 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// Create a custom polynomial for CRC-16
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [seed]: initial counter to start from
|
||||||
|
/// - [xorOut]: the value to xor with the final output
|
||||||
|
/// - [reversed]: to use reversed or reflected polynomial and input
|
||||||
|
CRC16Params(
|
||||||
|
this.poly, {
|
||||||
|
this.seed = 0,
|
||||||
|
this.xorOut = 0,
|
||||||
|
this.reversed = false,
|
||||||
|
}) : name = poly.toRadixString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-16 code generator with a polynomial.
|
||||||
|
abstract class CRC16Hash extends HashDigestSink {
|
||||||
|
final int seed;
|
||||||
|
final int xorOut;
|
||||||
|
final int polynomial;
|
||||||
|
final table = Uint16List(256);
|
||||||
|
|
||||||
|
int _crc;
|
||||||
|
|
||||||
|
/// Creates a sink for generating CRC-16 Hash
|
||||||
|
CRC16Hash(CRC16Params params)
|
||||||
|
: seed = params.seed,
|
||||||
|
xorOut = params.xorOut,
|
||||||
|
polynomial = params.poly,
|
||||||
|
_crc = params.seed {
|
||||||
|
$generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 2;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_crc = seed;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the lookup table for CRC-16
|
||||||
|
void $generate();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
_crc ^= xorOut;
|
||||||
|
return Uint8List.fromList([
|
||||||
|
_crc >>> 8,
|
||||||
|
_crc,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-16 code generator with a polynomial.
|
||||||
|
class CRC16NormalHash extends CRC16Hash {
|
||||||
|
/// Creates a sink for generating CRC-16 Hash
|
||||||
|
CRC16NormalHash(super.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $generate() {
|
||||||
|
int i, j, r, p;
|
||||||
|
p = polynomial & _mask16;
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
r = i << 8;
|
||||||
|
for (j = 0; j < 8; ++j) {
|
||||||
|
r = (((r >>> 15) & 1) * p) ^ (r << 1);
|
||||||
|
}
|
||||||
|
table[i] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (int index; start < end; start++) {
|
||||||
|
index = ((_crc >>> 8) ^ chunk[start]) & 0xFF;
|
||||||
|
_crc = table[index] ^ (_crc << 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-16 code generator with a reversed or reflected polynomial.
|
||||||
|
class CRC16ReverseHash extends CRC16Hash {
|
||||||
|
/// Creates a sink for generating CRC-16 Hash
|
||||||
|
CRC16ReverseHash(super.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $generate() {
|
||||||
|
int i, j, r, p;
|
||||||
|
|
||||||
|
// reverse polynomial
|
||||||
|
p = 0;
|
||||||
|
for (i = 0; i < 16; ++i) {
|
||||||
|
p ^= ((polynomial >>> i) & 1) << (15 - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate table
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
r = i;
|
||||||
|
for (j = 0; j < 8; ++j) {
|
||||||
|
r = ((r & 1) * p) ^ (r >>> 1);
|
||||||
|
}
|
||||||
|
table[i] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (int index; start < end; start++) {
|
||||||
|
index = (_crc ^ chunk[start]) & 0xFF;
|
||||||
|
_crc = table[index] ^ (_crc >>> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
215
hashlib/lib/src/algorithms/crc/crc32.dart
Normal file
215
hashlib/lib/src/algorithms/crc/crc32.dart
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
/// Predefined polynomials for CRC-32.
|
||||||
|
///
|
||||||
|
/// The predefined polynomials comes from various sources:
|
||||||
|
/// - https://crccalc.com/
|
||||||
|
/// - https://pkg.go.dev/hash/crc32
|
||||||
|
/// - https://crcmod.sourceforge.net/crcmod.predefined.html
|
||||||
|
/// - https://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
||||||
|
class CRC32Params {
|
||||||
|
/// IEEE is by far and away the most common CRC-32 polynomial.
|
||||||
|
/// Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, etc.
|
||||||
|
static const ieee = CRC32Params._(
|
||||||
|
'IEEE', 0x04C11DB7, 0xFFFFFFFF, true, 0xFFFFFFFF); // 0xCBF43926
|
||||||
|
|
||||||
|
/// Castagnoli's polynomial, used in iSCSI.
|
||||||
|
/// Has better error detection characteristics than IEEE.
|
||||||
|
/// https://dx.doi.org/10.1109/26.231911
|
||||||
|
static const castagnoli = CRC32Params._(
|
||||||
|
'Castagnoli', 0x1EDC6F41, 0xFFFFFFFF, true, 0xFFFFFFFF); // 0xE3069283
|
||||||
|
|
||||||
|
/// Koopman's polynomial.
|
||||||
|
/// Also has better error detection characteristics than IEEE.
|
||||||
|
/// https://dx.doi.org/10.1109/DSN.2002.1028931
|
||||||
|
static const koopman = CRC32Params._(
|
||||||
|
'Koopman', 0x741B8CD7, 0xFFFFFFFF, true, 0x00000000); // 0xD2C22F51
|
||||||
|
|
||||||
|
// 0x04C11DB7 group
|
||||||
|
static const bzip2 = CRC32Params._(
|
||||||
|
'BZIP2', 0x04C11DB7, 0xFFFFFFFF, false, 0xFFFFFFFF); // 0xFC891918
|
||||||
|
static const cksum = CRC32Params._(
|
||||||
|
'CKSUM', 0x04C11DB7, 0x00000000, false, 0xFFFFFFFF); // 0x765E7680
|
||||||
|
static const jamcrc = CRC32Params._(
|
||||||
|
'JAMCRC', 0x04C11DB7, 0xFFFFFFFF, true, 0x00000000); // 0x340BC6D9
|
||||||
|
static const mpeg2 = CRC32Params._(
|
||||||
|
'MPEG-2', 0x04C11DB7, 0xFFFFFFFF, false, 0x00000000); // 0x0376E6E7
|
||||||
|
|
||||||
|
// Others
|
||||||
|
static const aixm = CRC32Params._(
|
||||||
|
'AIXM', 0x814141AB, 0x00000000, false, 0x00000000); // 0x3010BF7F
|
||||||
|
static const autosar = CRC32Params._(
|
||||||
|
'AUTOSAR', 0xF4ACFB13, 0xFFFFFFFF, true, 0xFFFFFFFF); // 0x1697D06A
|
||||||
|
static const base91D = CRC32Params._(
|
||||||
|
'BASE91-D', 0xA833982B, 0xFFFFFFFF, true, 0xFFFFFFFF); // 0x87315576
|
||||||
|
static const cdRomEdc = CRC32Params._(
|
||||||
|
'CD-ROM-EDC', 0x8001801B, 0x00000000, true, 0x00000000); // 0x6EC2EDC4
|
||||||
|
static const xfer = CRC32Params._(
|
||||||
|
'XFER', 0x000000AF, 0x00000000, false, 0x00000000); // 0xBD0BE338
|
||||||
|
|
||||||
|
// aliases
|
||||||
|
static const iso = ieee;
|
||||||
|
static const isoHdlc = ieee;
|
||||||
|
static const adccp = ieee;
|
||||||
|
static const v42 = ieee;
|
||||||
|
static const xz = ieee;
|
||||||
|
static const pkzip = ieee;
|
||||||
|
static const iscsi = castagnoli;
|
||||||
|
static const interlaken = castagnoli;
|
||||||
|
static const nvme = castagnoli;
|
||||||
|
static const base91C = castagnoli;
|
||||||
|
static const mef = koopman;
|
||||||
|
static const aal5 = bzip2;
|
||||||
|
static const dectB = bzip2;
|
||||||
|
static const posix = cksum;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Implementation
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Polynomial name
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Polynomial value
|
||||||
|
final int poly;
|
||||||
|
|
||||||
|
/// Initial CRC
|
||||||
|
final int seed;
|
||||||
|
|
||||||
|
/// Output XOR value
|
||||||
|
final int xorOut;
|
||||||
|
|
||||||
|
/// To use the reverse of the polynomial
|
||||||
|
final bool reversed;
|
||||||
|
|
||||||
|
const CRC32Params._(
|
||||||
|
this.name,
|
||||||
|
this.poly,
|
||||||
|
this.seed,
|
||||||
|
this.reversed, [
|
||||||
|
this.xorOut = 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// Create a custom polynomial for CRC-32
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [seed]: initial counter to start from
|
||||||
|
/// - [xorOut]: the value to xor with the final output
|
||||||
|
/// - [reversed]: to use reversed or reflected polynomial and input
|
||||||
|
CRC32Params(
|
||||||
|
this.poly, {
|
||||||
|
this.seed = 0,
|
||||||
|
this.xorOut = 0,
|
||||||
|
this.reversed = false,
|
||||||
|
}) : name = poly.toRadixString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-32 code generator with a polynomial.
|
||||||
|
abstract class CRC32Hash extends HashDigestSink {
|
||||||
|
final int seed;
|
||||||
|
final int xorOut;
|
||||||
|
final int polynomial;
|
||||||
|
final table = Uint32List(256);
|
||||||
|
|
||||||
|
int _crc;
|
||||||
|
|
||||||
|
/// Creates a sink for generating CRC-32 Hash
|
||||||
|
CRC32Hash(CRC32Params params)
|
||||||
|
: seed = params.seed,
|
||||||
|
xorOut = params.xorOut,
|
||||||
|
polynomial = params.poly,
|
||||||
|
_crc = params.seed {
|
||||||
|
$generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 4;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_crc = seed;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the lookup table for CRC-32
|
||||||
|
void $generate();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
_crc ^= xorOut;
|
||||||
|
return Uint8List.fromList([
|
||||||
|
_crc >>> 24,
|
||||||
|
_crc >>> 16,
|
||||||
|
_crc >>> 8,
|
||||||
|
_crc,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-32 code generator with a polynomial.
|
||||||
|
class CRC32NormalHash extends CRC32Hash {
|
||||||
|
/// Creates a sink for generating CRC-32 Hash
|
||||||
|
CRC32NormalHash(super.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $generate() {
|
||||||
|
int i, j, r, p;
|
||||||
|
p = polynomial & _mask32;
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
r = i << 24;
|
||||||
|
for (j = 0; j < 8; ++j) {
|
||||||
|
r = (((r >>> 31) & 1) * p) ^ (r << 1);
|
||||||
|
}
|
||||||
|
table[i] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (int index; start < end; start++) {
|
||||||
|
index = ((_crc >>> 24) ^ chunk[start]) & 0xFF;
|
||||||
|
_crc = table[index] ^ (_crc << 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-32 code generator with a reversed or reflected polynomial.
|
||||||
|
class CRC32ReverseHash extends CRC32Hash {
|
||||||
|
/// Creates a sink for generating CRC-32 Hash
|
||||||
|
CRC32ReverseHash(super.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $generate() {
|
||||||
|
int i, j, r, p;
|
||||||
|
|
||||||
|
// reverse polynomial
|
||||||
|
p = 0;
|
||||||
|
for (i = 0; i < 32; ++i) {
|
||||||
|
p ^= ((polynomial >>> i) & 1) << (31 - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate table
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
r = i;
|
||||||
|
for (j = 0; j < 8; ++j) {
|
||||||
|
r = ((r & 1) * p) ^ (r >>> 1);
|
||||||
|
}
|
||||||
|
table[i] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (int index; start < end; start++) {
|
||||||
|
index = (_crc ^ chunk[start]) & 0xFF;
|
||||||
|
_crc = table[index] ^ (_crc >>> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
265
hashlib/lib/src/algorithms/crc/crc64.dart
Normal file
265
hashlib/lib/src/algorithms/crc/crc64.dart
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
const int _pow32 = 2 ^ 32;
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
/// Predefined polynomials for CRC-64.
|
||||||
|
///
|
||||||
|
/// The predefined polynomials comes from various sources:
|
||||||
|
/// - https://pkg.go.dev/hash/crc64
|
||||||
|
/// - https://crcmod.sourceforge.net/crcmod.predefined.html
|
||||||
|
/// - https://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
||||||
|
/// - https://github.com/emn178/js-crc/blob/master/src/models.js
|
||||||
|
class CRC64Params {
|
||||||
|
/// Defined in ISO 3309 (HDLC), Swiss-Prot/TrEMBL.
|
||||||
|
static const iso = CRC64Params._("ISO-HDLC", 0x00000000, 0x0000001B, true,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000); // 46a5a9388a5beffe
|
||||||
|
|
||||||
|
/// Defined in ECMA-182 p. 51, XZ Utils.
|
||||||
|
static const ecma = CRC64Params._("ECMA-182", 0x42F0E1EB, 0xA9EA3693, false,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000); // b90956c775a41001
|
||||||
|
|
||||||
|
static const goIso = CRC64Params._("GO-ISO", 0x00000000, 0x0000001B, true,
|
||||||
|
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); // 46a5a9388a5beffe
|
||||||
|
static const goEcma = CRC64Params._("GO-ECMA", 0x42F0E1EB, 0xA9EA3693, true,
|
||||||
|
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); // 995dc9bbdf1939fa
|
||||||
|
static const ms = CRC64Params._("MS", 0x259C84CB, 0xA6426349, true,
|
||||||
|
0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000); // 75d4b74f024eceea
|
||||||
|
static const nvme = CRC64Params._("NVME", 0xAD93D235, 0x94C93659, true,
|
||||||
|
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); // ae8b14860a799888
|
||||||
|
static const redis = CRC64Params._("Redis", 0xAD93D235, 0x94C935A9, true,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000); // e9c6d914c4b8d9ca
|
||||||
|
static const we = CRC64Params._("WE", 0x42F0E1EB, 0xA9EA3693, false,
|
||||||
|
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); // 62ec59e3f1a4f00a
|
||||||
|
static const xz = CRC64Params._("XZ", 0x42F0E1EB, 0xA9EA3693, true,
|
||||||
|
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); // 995dc9bbdf1939fa
|
||||||
|
static const jones = CRC64Params._("Jones", 0xAD93D235, 0x94C935A9, true,
|
||||||
|
0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000); // caa717168609f281
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Implementation
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Polynomial name
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Most significant 32-bytes of polynomial (first 32)
|
||||||
|
final int high;
|
||||||
|
|
||||||
|
/// Least significant 32-bytes of polynomial (last 32)
|
||||||
|
final int low;
|
||||||
|
|
||||||
|
/// Initial CRC (most significant 32-bytes)
|
||||||
|
final int seedHigh;
|
||||||
|
|
||||||
|
/// Initial CRC (least significant 32-bytes)
|
||||||
|
final int seedLow;
|
||||||
|
|
||||||
|
/// Output XOR value (least significant 32-bytes)
|
||||||
|
final int xorOutHigh;
|
||||||
|
|
||||||
|
/// Output XOR value (most significant 32-bytes)
|
||||||
|
final int xorOutLow;
|
||||||
|
|
||||||
|
/// To use the reverse of the polynomial
|
||||||
|
final bool reversed;
|
||||||
|
|
||||||
|
const CRC64Params._(
|
||||||
|
this.name,
|
||||||
|
this.high,
|
||||||
|
this.low,
|
||||||
|
this.reversed,
|
||||||
|
this.seedHigh,
|
||||||
|
this.seedLow, [
|
||||||
|
this.xorOutHigh = 0,
|
||||||
|
this.xorOutLow = 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/// Create a custom polynomial for CRC-64
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [seed]: initial counter to start from
|
||||||
|
/// - [xorOut]: the value to xor with the final output
|
||||||
|
/// - [reversed]: to use reversed or reflected polynomial and input
|
||||||
|
CRC64Params(
|
||||||
|
int poly, {
|
||||||
|
this.reversed = false,
|
||||||
|
int seed = 0,
|
||||||
|
int xorOut = 0,
|
||||||
|
}) : high = poly ~/ _pow32,
|
||||||
|
low = poly % _pow32,
|
||||||
|
seedHigh = seed ~/ _pow32,
|
||||||
|
seedLow = seed % _pow32,
|
||||||
|
xorOutHigh = xorOut ~/ _pow32,
|
||||||
|
xorOutLow = xorOut % _pow32,
|
||||||
|
name = poly.toRadixString(16);
|
||||||
|
|
||||||
|
/// Create a custom polynomial from hexadecimal
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [poly]: the polynomial in hexadecimal (MSB first)
|
||||||
|
/// - [seed]: initial counter to start from (MSB first)
|
||||||
|
/// - [xorOut]: the value to xor with the final output (MSB first)
|
||||||
|
/// - [reversed]: to use reversed or reflected polynomial and input
|
||||||
|
factory CRC64Params.hex({
|
||||||
|
required String poly,
|
||||||
|
bool reversed = false,
|
||||||
|
String seed = "0000000000000000",
|
||||||
|
String xorOut = "0000000000000000",
|
||||||
|
}) {
|
||||||
|
Uint8List p = fromHex(poly);
|
||||||
|
Uint8List s = fromHex(seed);
|
||||||
|
Uint8List x = fromHex(xorOut);
|
||||||
|
int high = (p[0] << 24) ^ (p[1] << 16) ^ (p[2] << 8) ^ (p[3]);
|
||||||
|
int low = (p[4] << 24) ^ (p[5] << 16) ^ (p[6] << 8) ^ (p[7]);
|
||||||
|
int seedHigh = (s[0] << 24) ^ (s[1] << 16) ^ (s[2] << 8) ^ (s[3]);
|
||||||
|
int seedLow = (s[4] << 24) ^ (s[5] << 16) ^ (s[6] << 8) ^ (s[7]);
|
||||||
|
int xorOutHigh = (x[0] << 24) ^ (x[1] << 16) ^ (x[2] << 8) ^ (x[3]);
|
||||||
|
int xorOutLow = (x[4] << 24) ^ (x[5] << 16) ^ (x[6] << 8) ^ (x[7]);
|
||||||
|
return CRC64Params._(
|
||||||
|
poly,
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
reversed,
|
||||||
|
seedHigh,
|
||||||
|
seedLow,
|
||||||
|
xorOutHigh,
|
||||||
|
xorOutLow,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-64 code generator with a polynomial.
|
||||||
|
abstract class CRC64Hash extends HashDigestSink {
|
||||||
|
final int polyHigh;
|
||||||
|
final int polyLow;
|
||||||
|
final int seedHigh;
|
||||||
|
final int seedLow;
|
||||||
|
final int xorOutHigh;
|
||||||
|
final int xorOutLow;
|
||||||
|
final table = Uint32List(512);
|
||||||
|
|
||||||
|
int _high, _low;
|
||||||
|
|
||||||
|
/// Creates a sink for generating CRC-64 Hash
|
||||||
|
CRC64Hash(CRC64Params params)
|
||||||
|
: polyHigh = params.high,
|
||||||
|
polyLow = params.low,
|
||||||
|
seedHigh = params.seedHigh,
|
||||||
|
seedLow = params.seedLow,
|
||||||
|
xorOutHigh = params.xorOutHigh,
|
||||||
|
xorOutLow = params.xorOutLow,
|
||||||
|
_high = params.seedHigh,
|
||||||
|
_low = params.seedLow {
|
||||||
|
$generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 8;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
super.reset();
|
||||||
|
_high = seedHigh;
|
||||||
|
_low = seedLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the lookup table for CRC-64
|
||||||
|
void $generate();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
_high ^= xorOutHigh;
|
||||||
|
_low ^= xorOutLow;
|
||||||
|
return Uint8List.fromList([
|
||||||
|
_high >>> 24,
|
||||||
|
_high >>> 16,
|
||||||
|
_high >>> 8,
|
||||||
|
_high,
|
||||||
|
_low >>> 24,
|
||||||
|
_low >>> 16,
|
||||||
|
_low >>> 8,
|
||||||
|
_low,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-64 code generator with a normal polynomial.
|
||||||
|
class CRC64NormalHash extends CRC64Hash {
|
||||||
|
/// Creates a sink for generating CRC-64 Hash
|
||||||
|
CRC64NormalHash(super.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $generate() {
|
||||||
|
int i, j, h, l, f, ph, pl;
|
||||||
|
ph = polyHigh & _mask32;
|
||||||
|
pl = polyLow & _mask32;
|
||||||
|
for (i = 0; i < 512; i += 2) {
|
||||||
|
h = i << 23; // (i / 2) << 24
|
||||||
|
l = 0;
|
||||||
|
for (j = 0; j < 8; ++j) {
|
||||||
|
f = (h >>> 31) & 1;
|
||||||
|
h = (f * ph) ^ (h << 1) ^ ((l >>> 31) & 1);
|
||||||
|
l = (f * pl) ^ (l << 1);
|
||||||
|
}
|
||||||
|
table[i] = h;
|
||||||
|
table[i + 1] = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (int i; start < end; start++) {
|
||||||
|
i = (((_high >>> 24) ^ chunk[start]) & 0xFF) << 1;
|
||||||
|
_high = table[i] ^ (_high << 8) ^ ((_low >>> 24) & 0xFF);
|
||||||
|
_low = table[i + 1] ^ (_low << 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A CRC-64 code generator with reversed or reflected polynomial.
|
||||||
|
class CRC64ReverseHash extends CRC64Hash {
|
||||||
|
/// Creates a sink for generating CRC-64 Hash
|
||||||
|
CRC64ReverseHash(super.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $generate() {
|
||||||
|
int i, j, h, l, f, ph, pl;
|
||||||
|
|
||||||
|
// reverse polynomial
|
||||||
|
ph = 0;
|
||||||
|
pl = 0;
|
||||||
|
for (i = 0; i < 32; ++i) {
|
||||||
|
pl ^= ((polyHigh >>> i) & 1) << (31 - i);
|
||||||
|
ph ^= ((polyLow >>> i) & 1) << (31 - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate table
|
||||||
|
for (i = 0; i < 512; i += 2) {
|
||||||
|
h = 0;
|
||||||
|
l = i >>> 1;
|
||||||
|
for (j = 0; j < 8; ++j) {
|
||||||
|
f = l & 1;
|
||||||
|
l = (f * pl) ^ ((h & 1) << 31) ^ ((l & _mask32) >>> 1);
|
||||||
|
h = (f * ph) ^ ((h & _mask32) >>> 1);
|
||||||
|
}
|
||||||
|
table[i] = h;
|
||||||
|
table[i + 1] = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (int i; start < end; start++) {
|
||||||
|
i = ((_low ^ chunk[start]) & 0xFF) << 1;
|
||||||
|
_low = table[i + 1] ^ ((_high & 0xFF) << 24) ^ ((_low & _mask32) >>> 8);
|
||||||
|
_high = table[i] ^ ((_high & _mask32) >>> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
hashlib/lib/src/algorithms/hmac.dart
Normal file
70
hashlib/lib/src/algorithms/hmac.dart
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
/// This implementation is derived from the RFC document
|
||||||
|
/// [HMAC: Keyed-Hashing for Message Authentication][rfc2104].
|
||||||
|
///
|
||||||
|
/// [rfc2104]: https://www.ietf.org/rfc/rfc2104.html
|
||||||
|
class HMACSink<T extends BlockHashBase> extends MACSinkBase {
|
||||||
|
final BlockHashSink _sink;
|
||||||
|
|
||||||
|
@override
|
||||||
|
late final int hashLength = _sink.hashLength;
|
||||||
|
|
||||||
|
late final innerKey = Uint8List(_sink.blockLength);
|
||||||
|
late final outerKey = Uint8List(_sink.blockLength);
|
||||||
|
|
||||||
|
HMACSink(
|
||||||
|
T algo,
|
||||||
|
List<int> keyBytes,
|
||||||
|
) : _sink = algo.createSink() {
|
||||||
|
var key = keyBytes is Uint8List ? keyBytes : Uint8List.fromList(keyBytes);
|
||||||
|
|
||||||
|
// Keys longer than blockLength are shortened by hashing them
|
||||||
|
if (key.length > _sink.blockLength) {
|
||||||
|
_sink.reset();
|
||||||
|
_sink.add(key);
|
||||||
|
key = _sink.$finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculated padded keys for inner and outer sinks
|
||||||
|
int i = 0;
|
||||||
|
for (; i < key.length; i++) {
|
||||||
|
innerKey[i] = key[i] ^ 0x36;
|
||||||
|
outerKey[i] = key[i] ^ 0x5c;
|
||||||
|
}
|
||||||
|
for (; i < _sink.blockLength; i++) {
|
||||||
|
innerKey[i] = 0x36;
|
||||||
|
outerKey[i] = 0x5c;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sink.reset();
|
||||||
|
_sink.add(innerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_sink.reset();
|
||||||
|
_sink.add(innerKey);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
_sink.$process(chunk, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
var hash = _sink.$finalize();
|
||||||
|
_sink.reset();
|
||||||
|
_sink.add(outerKey);
|
||||||
|
_sink.add(hash);
|
||||||
|
return _sink.$finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
42
hashlib/lib/src/algorithms/keccak/keccak.dart
Normal file
42
hashlib/lib/src/algorithms/keccak/keccak.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'keccak_64bit.dart' if (dart.library.js) 'keccak_32bit.dart';
|
||||||
|
|
||||||
|
export 'keccak_64bit.dart' if (dart.library.js) 'keccak_32bit.dart';
|
||||||
|
|
||||||
|
/// Implementation of Keccak-224 that generates a 224-bit hash.
|
||||||
|
class Keccak224Hash extends KeccakHash {
|
||||||
|
Keccak224Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 224 >>> 3,
|
||||||
|
paddingByte: 0x01,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of Keccak-256 that generates a 256-bit hash.
|
||||||
|
class Keccak256Hash extends KeccakHash {
|
||||||
|
Keccak256Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 256 >>> 3,
|
||||||
|
paddingByte: 0x01,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of Keccak-384 that generates a 384-bit hash.
|
||||||
|
class Keccak384Hash extends KeccakHash {
|
||||||
|
Keccak384Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 384 >>> 3,
|
||||||
|
paddingByte: 0x01,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of Keccak-512 that generates a 512-bit hash.
|
||||||
|
class Keccak512Hash extends KeccakHash {
|
||||||
|
Keccak512Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 512 >>> 3,
|
||||||
|
paddingByte: 0x01,
|
||||||
|
);
|
||||||
|
}
|
||||||
409
hashlib/lib/src/algorithms/keccak/keccak_32bit.dart
Normal file
409
hashlib/lib/src/algorithms/keccak/keccak_32bit.dart
Normal file
|
|
@ -0,0 +1,409 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
// Rotation constants
|
||||||
|
const int _rot01 = 36;
|
||||||
|
const int _rot02 = 3;
|
||||||
|
const int _rot03 = 41;
|
||||||
|
const int _rot04 = 18;
|
||||||
|
const int _rot05 = 1;
|
||||||
|
const int _rot06 = 44;
|
||||||
|
const int _rot07 = 10;
|
||||||
|
const int _rot08 = 45;
|
||||||
|
const int _rot09 = 2;
|
||||||
|
const int _rot10 = 62;
|
||||||
|
const int _rot11 = 6;
|
||||||
|
const int _rot12 = 43;
|
||||||
|
const int _rot13 = 15;
|
||||||
|
const int _rot14 = 61;
|
||||||
|
const int _rot15 = 28;
|
||||||
|
const int _rot16 = 55;
|
||||||
|
const int _rot17 = 25;
|
||||||
|
const int _rot18 = 21;
|
||||||
|
const int _rot19 = 56;
|
||||||
|
const int _rot20 = 27;
|
||||||
|
const int _rot21 = 20;
|
||||||
|
const int _rot22 = 39;
|
||||||
|
const int _rot23 = 8;
|
||||||
|
const int _rot24 = 14;
|
||||||
|
|
||||||
|
// 200-bit round constants iota mapping [little endian]
|
||||||
|
const _rc = <int>[
|
||||||
|
0x00000001, 0x00000000, //
|
||||||
|
0x00008082, 0x00000000,
|
||||||
|
0x0000808a, 0x80000000,
|
||||||
|
0x80008000, 0x80000000,
|
||||||
|
0x0000808b, 0x00000000,
|
||||||
|
0x80000001, 0x00000000,
|
||||||
|
0x80008081, 0x80000000,
|
||||||
|
0x00008009, 0x80000000,
|
||||||
|
0x0000008a, 0x00000000,
|
||||||
|
0x00000088, 0x00000000,
|
||||||
|
0x80008009, 0x00000000,
|
||||||
|
0x8000000a, 0x00000000,
|
||||||
|
0x8000808b, 0x00000000,
|
||||||
|
0x0000008b, 0x80000000,
|
||||||
|
0x00008089, 0x80000000,
|
||||||
|
0x00008003, 0x80000000,
|
||||||
|
0x00008002, 0x80000000,
|
||||||
|
0x00000080, 0x80000000,
|
||||||
|
0x0000800a, 0x00000000,
|
||||||
|
0x8000000a, 0x80000000,
|
||||||
|
0x80008081, 0x80000000,
|
||||||
|
0x00008080, 0x80000000,
|
||||||
|
0x80000001, 0x00000000,
|
||||||
|
0x80008008, 0x80000000,
|
||||||
|
];
|
||||||
|
|
||||||
|
// State variable indices
|
||||||
|
const int _a0 = 0;
|
||||||
|
const int _a1 = _a0 + 2;
|
||||||
|
const int _a2 = _a1 + 2;
|
||||||
|
const int _a3 = _a2 + 2;
|
||||||
|
const int _a4 = _a3 + 2;
|
||||||
|
const int _a5 = _a4 + 2;
|
||||||
|
const int _a6 = _a5 + 2;
|
||||||
|
const int _a7 = _a6 + 2;
|
||||||
|
const int _a8 = _a7 + 2;
|
||||||
|
const int _a9 = _a8 + 2;
|
||||||
|
const int _a10 = _a9 + 2;
|
||||||
|
const int _a11 = _a10 + 2;
|
||||||
|
const int _a12 = _a11 + 2;
|
||||||
|
const int _a13 = _a12 + 2;
|
||||||
|
const int _a14 = _a13 + 2;
|
||||||
|
const int _a15 = _a14 + 2;
|
||||||
|
const int _a16 = _a15 + 2;
|
||||||
|
const int _a17 = _a16 + 2;
|
||||||
|
const int _a18 = _a17 + 2;
|
||||||
|
const int _a19 = _a18 + 2;
|
||||||
|
const int _a20 = _a19 + 2;
|
||||||
|
const int _a21 = _a20 + 2;
|
||||||
|
const int _a22 = _a21 + 2;
|
||||||
|
const int _a23 = _a22 + 2;
|
||||||
|
const int _a24 = _a23 + 2;
|
||||||
|
|
||||||
|
// Temp variable indices
|
||||||
|
const int _b0 = 0;
|
||||||
|
const int _b1 = _b0 + 2;
|
||||||
|
const int _b2 = _b1 + 2;
|
||||||
|
const int _b3 = _b2 + 2;
|
||||||
|
const int _b4 = _b3 + 2;
|
||||||
|
const int _b5 = _b4 + 2;
|
||||||
|
const int _b6 = _b5 + 2;
|
||||||
|
const int _b7 = _b6 + 2;
|
||||||
|
const int _b8 = _b7 + 2;
|
||||||
|
const int _b9 = _b8 + 2;
|
||||||
|
const int _b10 = _b9 + 2;
|
||||||
|
const int _b11 = _b10 + 2;
|
||||||
|
const int _b12 = _b11 + 2;
|
||||||
|
const int _b13 = _b12 + 2;
|
||||||
|
const int _b14 = _b13 + 2;
|
||||||
|
const int _b15 = _b14 + 2;
|
||||||
|
const int _b16 = _b15 + 2;
|
||||||
|
const int _b17 = _b16 + 2;
|
||||||
|
const int _b18 = _b17 + 2;
|
||||||
|
const int _b19 = _b18 + 2;
|
||||||
|
const int _b20 = _b19 + 2;
|
||||||
|
const int _b21 = _b20 + 2;
|
||||||
|
const int _b22 = _b21 + 2;
|
||||||
|
const int _b23 = _b22 + 2;
|
||||||
|
const int _b24 = _b23 + 2;
|
||||||
|
const int _c0 = _b24 + 2;
|
||||||
|
const int _c1 = _c0 + 2;
|
||||||
|
const int _c2 = _c1 + 2;
|
||||||
|
const int _c3 = _c2 + 2;
|
||||||
|
const int _c4 = _c3 + 2;
|
||||||
|
const int _d = _c4 + 2;
|
||||||
|
|
||||||
|
/// This is an implementation of Keccak-f\[1600\] derived from
|
||||||
|
/// [FIPS-202 SHA-3 Standard][fips202] published by NIST.
|
||||||
|
///
|
||||||
|
/// Followed the optimizations in [PyCryptodome's implementation][keccak].
|
||||||
|
/// Special thanks to [tiny_sha3] for readable code and test cases.
|
||||||
|
///
|
||||||
|
/// It uses 32-bit integers to accommodate 64-bit integer operations, designed
|
||||||
|
/// specially to be supported by Web VM. It is albeit slower than the native
|
||||||
|
/// implementation.
|
||||||
|
///
|
||||||
|
/// [fips202]: https://csrc.nist.gov/publications/detail/fips/202/final
|
||||||
|
/// [keccak]: https://github.com/Legrandin/pycryptodome/blob/master/src/keccak.c
|
||||||
|
/// [tiny_sha3]: https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
|
||||||
|
class KeccakHash extends BlockHashSink {
|
||||||
|
final int stateSize;
|
||||||
|
final int paddingByte;
|
||||||
|
final _var = Uint32List(_d + 2);
|
||||||
|
late final Uint32List state = sbuffer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
KeccakHash({
|
||||||
|
required this.stateSize,
|
||||||
|
required this.paddingByte,
|
||||||
|
int? outputSize, // equals to state size if not provided
|
||||||
|
}) : assert(stateSize >= 0 && stateSize < 100),
|
||||||
|
hashLength = outputSize ?? stateSize,
|
||||||
|
super(
|
||||||
|
200 - (stateSize << 1), // rate as blockLength
|
||||||
|
bufferLength: 200, // 1600-bit state as buffer
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
buffer.fillRange(0, buffer.length, 0);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] ^= chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (x << n) | (x >>> (64 - n))
|
||||||
|
static void _rotl(int n, List<int> x, int i, List<int> z, int k) {
|
||||||
|
// *numbers are in little-endian order*
|
||||||
|
var a = x[i];
|
||||||
|
var b = x[i + 1];
|
||||||
|
if (n == 32) {
|
||||||
|
z[k] = b;
|
||||||
|
z[k + 1] = a;
|
||||||
|
} else if (n < 32) {
|
||||||
|
z[k] = (a << n) | (b >>> (32 - n));
|
||||||
|
z[k + 1] = (b << n) | a >>> (32 - n);
|
||||||
|
} else {
|
||||||
|
z[k] = (b << (n - 32)) | (a >>> (64 - n));
|
||||||
|
z[k + 1] = (a << (n - 32)) | (b >>> (64 - n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = x ^ y
|
||||||
|
static void _xor(List<int> x, int i, List<int> y, int j, List<int> z, int k) {
|
||||||
|
z[k] = x[i] ^ y[j];
|
||||||
|
z[k + 1] = x[i + 1] ^ y[j + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = x[i1] ^ x[i2] ^ x[i3] ^ x[i4] ^ x[i5]
|
||||||
|
static void _xor5(
|
||||||
|
List<int> x, int i1, int i2, int i3, int i4, int i5, List<int> z, int k) {
|
||||||
|
z[k] = x[i1] ^ x[i2] ^ x[i3] ^ x[i4] ^ x[i5];
|
||||||
|
z[k + 1] = x[i1 + 1] ^ x[i2 + 1] ^ x[i3 + 1] ^ x[i4 + 1] ^ x[i5 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = x[i1] ^ (~x[i2] & x[i3]);
|
||||||
|
static void _chi(List<int> x, int i1, int i2, int i3, List<int> z, int k) {
|
||||||
|
z[k] = x[i1] ^ (~x[i2] & x[i3]);
|
||||||
|
z[k + 1] = x[i1 + 1] ^ (~x[i2 + 1] & x[i3 + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
for (int r = 0; r < _rc.length; r += 2) {
|
||||||
|
// ---- Theta parity ----
|
||||||
|
// c0 = a0 ^ a5 ^ a10 ^ a15 ^ a20;
|
||||||
|
_xor5(state, _a0, _a5, _a10, _a15, _a20, _var, _c0);
|
||||||
|
// c1 = a1 ^ a6 ^ a11 ^ a16 ^ a21;
|
||||||
|
_xor5(state, _a1, _a6, _a11, _a16, _a21, _var, _c1);
|
||||||
|
// c2 = a2 ^ a7 ^ a12 ^ a17 ^ a22;
|
||||||
|
_xor5(state, _a2, _a7, _a12, _a17, _a22, _var, _c2);
|
||||||
|
// c3 = a3 ^ a8 ^ a13 ^ a18 ^ a23;
|
||||||
|
_xor5(state, _a3, _a8, _a13, _a18, _a23, _var, _c3);
|
||||||
|
// c4 = a4 ^ a9 ^ a14 ^ a19 ^ a24;
|
||||||
|
_xor5(state, _a4, _a9, _a14, _a19, _a24, _var, _c4);
|
||||||
|
|
||||||
|
// ---- Theta + Rho + Pi ----
|
||||||
|
// d = c4 ^ _rotl(c1, 1);
|
||||||
|
_rotl(1, _var, _c1, _var, _d);
|
||||||
|
_xor(_var, _c4, _var, _d, _var, _d);
|
||||||
|
// b0 = d ^ a0;
|
||||||
|
_xor(_var, _d, state, _a0, _var, _b0);
|
||||||
|
// b16 = _rotl(d ^ a5, _rot01);
|
||||||
|
_xor(_var, _d, state, _a5, _var, _b16);
|
||||||
|
_rotl(_rot01, _var, _b16, _var, _b16);
|
||||||
|
// b7 = _rotl(d ^ a10, _rot02);
|
||||||
|
_xor(_var, _d, state, _a10, _var, _b7);
|
||||||
|
_rotl(_rot02, _var, _b7, _var, _b7);
|
||||||
|
// b23 = _rotl(d ^ a15, _rot03);
|
||||||
|
_xor(_var, _d, state, _a15, _var, _b23);
|
||||||
|
_rotl(_rot03, _var, _b23, _var, _b23);
|
||||||
|
// b14 = _rotl(d ^ a20, _rot04);
|
||||||
|
_xor(_var, _d, state, _a20, _var, _b14);
|
||||||
|
_rotl(_rot04, _var, _b14, _var, _b14);
|
||||||
|
|
||||||
|
// d = c0 ^ _rotl(c2, 1);
|
||||||
|
_rotl(1, _var, _c2, _var, _d);
|
||||||
|
_xor(_var, _c0, _var, _d, _var, _d);
|
||||||
|
// b10 = _rotl(d ^ a1, _rot05);
|
||||||
|
_xor(_var, _d, state, _a1, _var, _b10);
|
||||||
|
_rotl(_rot05, _var, _b10, _var, _b10);
|
||||||
|
// b1 = _rotl(d ^ a6, _rot06);
|
||||||
|
_xor(_var, _d, state, _a6, _var, _b1);
|
||||||
|
_rotl(_rot06, _var, _b1, _var, _b1);
|
||||||
|
// b17 = _rotl(d ^ a11, _rot07);
|
||||||
|
_xor(_var, _d, state, _a11, _var, _b17);
|
||||||
|
_rotl(_rot07, _var, _b17, _var, _b17);
|
||||||
|
// b8 = _rotl(d ^ a16, _rot08);
|
||||||
|
_xor(_var, _d, state, _a16, _var, _b8);
|
||||||
|
_rotl(_rot08, _var, _b8, _var, _b8);
|
||||||
|
// b24 = _rotl(d ^ a21, _rot09);
|
||||||
|
_xor(_var, _d, state, _a21, _var, _b24);
|
||||||
|
_rotl(_rot09, _var, _b24, _var, _b24);
|
||||||
|
|
||||||
|
// d = c1 ^ _rotl(c3, 1);
|
||||||
|
_rotl(1, _var, _c3, _var, _d);
|
||||||
|
_xor(_var, _d, _var, _c1, _var, _d);
|
||||||
|
// b20 = _rotl(d ^ a2, _rot10);
|
||||||
|
_xor(_var, _d, state, _a2, _var, _b20);
|
||||||
|
_rotl(_rot10, _var, _b20, _var, _b20);
|
||||||
|
// b11 = _rotl(d ^ a7, _rot11);
|
||||||
|
_xor(_var, _d, state, _a7, _var, _b11);
|
||||||
|
_rotl(_rot11, _var, _b11, _var, _b11);
|
||||||
|
// b2 = _rotl(d ^ a12, _rot12);
|
||||||
|
_xor(_var, _d, state, _a12, _var, _b2);
|
||||||
|
_rotl(_rot12, _var, _b2, _var, _b2);
|
||||||
|
// b18 = _rotl(d ^ a17, _rot13);
|
||||||
|
_xor(_var, _d, state, _a17, _var, _b18);
|
||||||
|
_rotl(_rot13, _var, _b18, _var, _b18);
|
||||||
|
// b9 = _rotl(d ^ a22, _rot14);
|
||||||
|
_xor(_var, _d, state, _a22, _var, _b9);
|
||||||
|
_rotl(_rot14, _var, _b9, _var, _b9);
|
||||||
|
|
||||||
|
// d = c2 ^ _rotl(c4, 1);
|
||||||
|
_rotl(1, _var, _c4, _var, _d);
|
||||||
|
_xor(_var, _c2, _var, _d, _var, _d);
|
||||||
|
// b5 = _rotl(d ^ a3, _rot15);
|
||||||
|
_xor(_var, _d, state, _a3, _var, _b5);
|
||||||
|
_rotl(_rot15, _var, _b5, _var, _b5);
|
||||||
|
// b21 = _rotl(d ^ a8, _rot16);
|
||||||
|
_xor(_var, _d, state, _a8, _var, _b21);
|
||||||
|
_rotl(_rot16, _var, _b21, _var, _b21);
|
||||||
|
// b12 = _rotl(d ^ a13, _rot17);
|
||||||
|
_xor(_var, _d, state, _a13, _var, _b12);
|
||||||
|
_rotl(_rot17, _var, _b12, _var, _b12);
|
||||||
|
// b3 = _rotl(d ^ a18, _rot18);
|
||||||
|
_xor(_var, _d, state, _a18, _var, _b3);
|
||||||
|
_rotl(_rot18, _var, _b3, _var, _b3);
|
||||||
|
// b19 = _rotl(d ^ a23, _rot19);
|
||||||
|
_xor(_var, _d, state, _a23, _var, _b19);
|
||||||
|
_rotl(_rot19, _var, _b19, _var, _b19);
|
||||||
|
|
||||||
|
// d = c3 ^ _rotl(c0, 1);
|
||||||
|
_rotl(1, _var, _c0, _var, _d);
|
||||||
|
_xor(_var, _c3, _var, _d, _var, _d);
|
||||||
|
// b15 = _rotl(d ^ a4, _rot20);
|
||||||
|
_xor(_var, _d, state, _a4, _var, _b15);
|
||||||
|
_rotl(_rot20, _var, _b15, _var, _b15);
|
||||||
|
// b6 = _rotl(d ^ a9, _rot21);
|
||||||
|
_xor(_var, _d, state, _a9, _var, _b6);
|
||||||
|
_rotl(_rot21, _var, _b6, _var, _b6);
|
||||||
|
// b22 = _rotl(d ^ a14, _rot22);
|
||||||
|
_xor(_var, _d, state, _a14, _var, _b22);
|
||||||
|
_rotl(_rot22, _var, _b22, _var, _b22);
|
||||||
|
// b13 = _rotl(d ^ a19, _rot23);
|
||||||
|
_xor(_var, _d, state, _a19, _var, _b13);
|
||||||
|
_rotl(_rot23, _var, _b13, _var, _b13);
|
||||||
|
// b4 = _rotl(d ^ a24, _rot24);
|
||||||
|
_xor(_var, _d, state, _a24, _var, _b4);
|
||||||
|
_rotl(_rot24, _var, _b4, _var, _b4);
|
||||||
|
|
||||||
|
// ---- Chi + Iota ----
|
||||||
|
// a0 = b0 ^ (~b1 & b2) ^ r;
|
||||||
|
_chi(_var, _b0, _b1, _b2, state, _a0);
|
||||||
|
_xor(state, _a0, _rc, r, state, _a0);
|
||||||
|
// a1 = b1 ^ (~b2 & b3);
|
||||||
|
_chi(_var, _b1, _b2, _b3, state, _a1);
|
||||||
|
// a2 = b2 ^ (~b3 & b4);
|
||||||
|
_chi(_var, _b2, _b3, _b4, state, _a2);
|
||||||
|
// a3 = b3 ^ (~b4 & b0);
|
||||||
|
_chi(_var, _b3, _b4, _b0, state, _a3);
|
||||||
|
// a4 = b4 ^ (~b0 & b1);
|
||||||
|
_chi(_var, _b4, _b0, _b1, state, _a4);
|
||||||
|
|
||||||
|
// a5 = b5 ^ (~b6 & b7);
|
||||||
|
_chi(_var, _b5, _b6, _b7, state, _a5);
|
||||||
|
// a6 = b6 ^ (~b7 & b8);
|
||||||
|
_chi(_var, _b6, _b7, _b8, state, _a6);
|
||||||
|
// a7 = b7 ^ (~b8 & b9);
|
||||||
|
_chi(_var, _b7, _b8, _b9, state, _a7);
|
||||||
|
// a8 = b8 ^ (~b9 & b5);
|
||||||
|
_chi(_var, _b8, _b9, _b5, state, _a8);
|
||||||
|
// a9 = b9 ^ (~b5 & b6);
|
||||||
|
_chi(_var, _b9, _b5, _b6, state, _a9);
|
||||||
|
|
||||||
|
// a10 = b10 ^ (~b11 & b12);
|
||||||
|
_chi(_var, _b10, _b11, _b12, state, _a10);
|
||||||
|
// a11 = b11 ^ (~b12 & b13);
|
||||||
|
_chi(_var, _b11, _b12, _b13, state, _a11);
|
||||||
|
// a12 = b12 ^ (~b13 & b14);
|
||||||
|
_chi(_var, _b12, _b13, _b14, state, _a12);
|
||||||
|
// a13 = b13 ^ (~b14 & b10);
|
||||||
|
_chi(_var, _b13, _b14, _b10, state, _a13);
|
||||||
|
// a14 = b14 ^ (~b10 & b11);
|
||||||
|
_chi(_var, _b14, _b10, _b11, state, _a14);
|
||||||
|
|
||||||
|
// a15 = b15 ^ (~b16 & b17);
|
||||||
|
_chi(_var, _b15, _b16, _b17, state, _a15);
|
||||||
|
// a16 = b16 ^ (~b17 & b18);
|
||||||
|
_chi(_var, _b16, _b17, _b18, state, _a16);
|
||||||
|
// a17 = b17 ^ (~b18 & b19);
|
||||||
|
_chi(_var, _b17, _b18, _b19, state, _a17);
|
||||||
|
// a18 = b18 ^ (~b19 & b15);
|
||||||
|
_chi(_var, _b18, _b19, _b15, state, _a18);
|
||||||
|
// a19 = b19 ^ (~b15 & b16);
|
||||||
|
_chi(_var, _b19, _b15, _b16, state, _a19);
|
||||||
|
|
||||||
|
// a20 = b20 ^ (~b21 & b22);
|
||||||
|
_chi(_var, _b20, _b21, _b22, state, _a20);
|
||||||
|
// a21 = b21 ^ (~b22 & b23);
|
||||||
|
_chi(_var, _b21, _b22, _b23, state, _a21);
|
||||||
|
// a22 = b22 ^ (~b23 & b24);
|
||||||
|
_chi(_var, _b22, _b23, _b24, state, _a22);
|
||||||
|
// a23 = b23 ^ (~b24 & b20);
|
||||||
|
_chi(_var, _b23, _b24, _b20, state, _a23);
|
||||||
|
// a24 = b24 ^ (~b20 & b21);
|
||||||
|
_chi(_var, _b24, _b20, _b21, state, _a24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Update the final block
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting the signature bytes
|
||||||
|
buffer[pos] ^= paddingByte;
|
||||||
|
buffer[blockLength - 1] ^= 0x80;
|
||||||
|
$update();
|
||||||
|
|
||||||
|
if (hashLength <= stateSize) {
|
||||||
|
return buffer.sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sponge construction
|
||||||
|
var bytes = Uint8List(hashLength);
|
||||||
|
for (int i = 0, j = 0; i < hashLength; i++, j++) {
|
||||||
|
if (j == blockLength) {
|
||||||
|
$update();
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
bytes[i] = buffer[j];
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
282
hashlib/lib/src/algorithms/keccak/keccak_64bit.dart
Normal file
282
hashlib/lib/src/algorithms/keccak/keccak_64bit.dart
Normal file
|
|
@ -0,0 +1,282 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
// Rotation constants
|
||||||
|
const int _rot01 = 36;
|
||||||
|
const int _rot02 = 3;
|
||||||
|
const int _rot03 = 41;
|
||||||
|
const int _rot04 = 18;
|
||||||
|
const int _rot05 = 1;
|
||||||
|
const int _rot06 = 44;
|
||||||
|
const int _rot07 = 10;
|
||||||
|
const int _rot08 = 45;
|
||||||
|
const int _rot09 = 2;
|
||||||
|
const int _rot10 = 62;
|
||||||
|
const int _rot11 = 6;
|
||||||
|
const int _rot12 = 43;
|
||||||
|
const int _rot13 = 15;
|
||||||
|
const int _rot14 = 61;
|
||||||
|
const int _rot15 = 28;
|
||||||
|
const int _rot16 = 55;
|
||||||
|
const int _rot17 = 25;
|
||||||
|
const int _rot18 = 21;
|
||||||
|
const int _rot19 = 56;
|
||||||
|
const int _rot20 = 27;
|
||||||
|
const int _rot21 = 20;
|
||||||
|
const int _rot22 = 39;
|
||||||
|
const int _rot23 = 8;
|
||||||
|
const int _rot24 = 14;
|
||||||
|
|
||||||
|
// 200-bit round constants iota mapping
|
||||||
|
const List<int> _rc = <int>[
|
||||||
|
0x0000000000000001,
|
||||||
|
0x0000000000008082,
|
||||||
|
0x800000000000808a,
|
||||||
|
0x8000000080008000,
|
||||||
|
0x000000000000808b,
|
||||||
|
0x0000000080000001,
|
||||||
|
0x8000000080008081,
|
||||||
|
0x8000000000008009,
|
||||||
|
0x000000000000008a,
|
||||||
|
0x0000000000000088,
|
||||||
|
0x0000000080008009,
|
||||||
|
0x000000008000000a,
|
||||||
|
0x000000008000808b,
|
||||||
|
0x800000000000008b,
|
||||||
|
0x8000000000008089,
|
||||||
|
0x8000000000008003,
|
||||||
|
0x8000000000008002,
|
||||||
|
0x8000000000000080,
|
||||||
|
0x000000000000800a,
|
||||||
|
0x800000008000000a,
|
||||||
|
0x8000000080008081,
|
||||||
|
0x8000000000008080,
|
||||||
|
0x0000000080000001,
|
||||||
|
0x8000000080008008,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This is an implementation of Keccak-f\[1600\] derived from
|
||||||
|
/// [FIPS-202 SHA-3 Standard][fips202] published by NIST.
|
||||||
|
///
|
||||||
|
/// Followed the optimizations in [PyCryptodome's implementation][keccak].
|
||||||
|
/// Special thanks to [tiny_sha3] for readable code and test cases.
|
||||||
|
///
|
||||||
|
/// It uses 64-bit integer operations internally which is not supported by
|
||||||
|
/// Web VM, but a lot faster.
|
||||||
|
///
|
||||||
|
/// [fips202]: https://csrc.nist.gov/publications/detail/fips/202/final
|
||||||
|
/// [keccak]: https://github.com/Legrandin/pycryptodome/blob/master/src/keccak.c
|
||||||
|
/// [tiny_sha3]: https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
|
||||||
|
class KeccakHash extends BlockHashSink {
|
||||||
|
final int stateSize;
|
||||||
|
final int paddingByte;
|
||||||
|
late final Uint64List qstate = Uint64List.view(buffer.buffer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
KeccakHash({
|
||||||
|
required this.stateSize,
|
||||||
|
required this.paddingByte,
|
||||||
|
int? outputSize, // equals to state size if not provided
|
||||||
|
}) : assert(stateSize >= 0 && stateSize < 100),
|
||||||
|
hashLength = outputSize ?? stateSize,
|
||||||
|
super(
|
||||||
|
200 - (stateSize << 1), // rate as blockLength
|
||||||
|
bufferLength: 200, // 1600-bit state as buffer
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
buffer.fillRange(0, buffer.length, 0);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] ^= chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Setting the signature bytes
|
||||||
|
buffer[pos] ^= paddingByte;
|
||||||
|
buffer[blockLength - 1] ^= 0x80;
|
||||||
|
$update();
|
||||||
|
|
||||||
|
if (hashLength <= stateSize) {
|
||||||
|
return buffer.sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sponge construction
|
||||||
|
var bytes = Uint8List(hashLength);
|
||||||
|
for (int i = 0, j = 0; i < hashLength; i++, j++) {
|
||||||
|
if (j == blockLength) {
|
||||||
|
$update();
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
bytes[i] = buffer[j];
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
// Use variables to avoid index processing
|
||||||
|
int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12;
|
||||||
|
int a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24;
|
||||||
|
|
||||||
|
int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12;
|
||||||
|
int b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24;
|
||||||
|
|
||||||
|
int c0, c1, c2, c3, c4, d, r;
|
||||||
|
|
||||||
|
// Prepare the state (little-endian)
|
||||||
|
a0 = qstate[0];
|
||||||
|
a1 = qstate[1];
|
||||||
|
a2 = qstate[2];
|
||||||
|
a3 = qstate[3];
|
||||||
|
a4 = qstate[4];
|
||||||
|
a5 = qstate[5];
|
||||||
|
a6 = qstate[6];
|
||||||
|
a7 = qstate[7];
|
||||||
|
a8 = qstate[8];
|
||||||
|
a9 = qstate[9];
|
||||||
|
a10 = qstate[10];
|
||||||
|
a11 = qstate[11];
|
||||||
|
a12 = qstate[12];
|
||||||
|
a13 = qstate[13];
|
||||||
|
a14 = qstate[14];
|
||||||
|
a15 = qstate[15];
|
||||||
|
a16 = qstate[16];
|
||||||
|
a17 = qstate[17];
|
||||||
|
a18 = qstate[18];
|
||||||
|
a19 = qstate[19];
|
||||||
|
a20 = qstate[20];
|
||||||
|
a21 = qstate[21];
|
||||||
|
a22 = qstate[22];
|
||||||
|
a23 = qstate[23];
|
||||||
|
a24 = qstate[24];
|
||||||
|
|
||||||
|
for (r in _rc) {
|
||||||
|
// ---- Theta parity ----
|
||||||
|
c0 = a0 ^ a5 ^ a10 ^ a15 ^ a20;
|
||||||
|
c1 = a1 ^ a6 ^ a11 ^ a16 ^ a21;
|
||||||
|
c2 = a2 ^ a7 ^ a12 ^ a17 ^ a22;
|
||||||
|
c3 = a3 ^ a8 ^ a13 ^ a18 ^ a23;
|
||||||
|
c4 = a4 ^ a9 ^ a14 ^ a19 ^ a24;
|
||||||
|
|
||||||
|
// ---- Theta + Rho + Pi ----
|
||||||
|
d = c4 ^ _rotl(c1, 1);
|
||||||
|
b0 = d ^ a0;
|
||||||
|
b16 = _rotl(d ^ a5, _rot01);
|
||||||
|
b7 = _rotl(d ^ a10, _rot02);
|
||||||
|
b23 = _rotl(d ^ a15, _rot03);
|
||||||
|
b14 = _rotl(d ^ a20, _rot04);
|
||||||
|
|
||||||
|
d = c0 ^ _rotl(c2, 1);
|
||||||
|
b10 = _rotl(d ^ a1, _rot05);
|
||||||
|
b1 = _rotl(d ^ a6, _rot06);
|
||||||
|
b17 = _rotl(d ^ a11, _rot07);
|
||||||
|
b8 = _rotl(d ^ a16, _rot08);
|
||||||
|
b24 = _rotl(d ^ a21, _rot09);
|
||||||
|
|
||||||
|
d = c1 ^ _rotl(c3, 1);
|
||||||
|
b20 = _rotl(d ^ a2, _rot10);
|
||||||
|
b11 = _rotl(d ^ a7, _rot11);
|
||||||
|
b2 = _rotl(d ^ a12, _rot12);
|
||||||
|
b18 = _rotl(d ^ a17, _rot13);
|
||||||
|
b9 = _rotl(d ^ a22, _rot14);
|
||||||
|
|
||||||
|
d = c2 ^ _rotl(c4, 1);
|
||||||
|
b5 = _rotl(d ^ a3, _rot15);
|
||||||
|
b21 = _rotl(d ^ a8, _rot16);
|
||||||
|
b12 = _rotl(d ^ a13, _rot17);
|
||||||
|
b3 = _rotl(d ^ a18, _rot18);
|
||||||
|
b19 = _rotl(d ^ a23, _rot19);
|
||||||
|
|
||||||
|
d = c3 ^ _rotl(c0, 1);
|
||||||
|
b15 = _rotl(d ^ a4, _rot20);
|
||||||
|
b6 = _rotl(d ^ a9, _rot21);
|
||||||
|
b22 = _rotl(d ^ a14, _rot22);
|
||||||
|
b13 = _rotl(d ^ a19, _rot23);
|
||||||
|
b4 = _rotl(d ^ a24, _rot24);
|
||||||
|
|
||||||
|
// ---- Chi + Iota ----
|
||||||
|
a0 = b0 ^ ((~b1) & b2) ^ r;
|
||||||
|
a1 = b1 ^ ((~b2) & b3);
|
||||||
|
a2 = b2 ^ ((~b3) & b4);
|
||||||
|
a3 = b3 ^ ((~b4) & b0);
|
||||||
|
a4 = b4 ^ ((~b0) & b1);
|
||||||
|
|
||||||
|
a5 = b5 ^ ((~b6) & b7);
|
||||||
|
a6 = b6 ^ ((~b7) & b8);
|
||||||
|
a7 = b7 ^ ((~b8) & b9);
|
||||||
|
a8 = b8 ^ ((~b9) & b5);
|
||||||
|
a9 = b9 ^ ((~b5) & b6);
|
||||||
|
|
||||||
|
a10 = b10 ^ ((~b11) & b12);
|
||||||
|
a11 = b11 ^ ((~b12) & b13);
|
||||||
|
a12 = b12 ^ ((~b13) & b14);
|
||||||
|
a13 = b13 ^ ((~b14) & b10);
|
||||||
|
a14 = b14 ^ ((~b10) & b11);
|
||||||
|
|
||||||
|
a15 = b15 ^ ((~b16) & b17);
|
||||||
|
a16 = b16 ^ ((~b17) & b18);
|
||||||
|
a17 = b17 ^ ((~b18) & b19);
|
||||||
|
a18 = b18 ^ ((~b19) & b15);
|
||||||
|
a19 = b19 ^ ((~b15) & b16);
|
||||||
|
|
||||||
|
a20 = b20 ^ ((~b21) & b22);
|
||||||
|
a21 = b21 ^ ((~b22) & b23);
|
||||||
|
a22 = b22 ^ ((~b23) & b24);
|
||||||
|
a23 = b23 ^ ((~b24) & b20);
|
||||||
|
a24 = b24 ^ ((~b20) & b21);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the state (little-endian)
|
||||||
|
qstate[0] = a0;
|
||||||
|
qstate[1] = a1;
|
||||||
|
qstate[2] = a2;
|
||||||
|
qstate[3] = a3;
|
||||||
|
qstate[4] = a4;
|
||||||
|
qstate[5] = a5;
|
||||||
|
qstate[6] = a6;
|
||||||
|
qstate[7] = a7;
|
||||||
|
qstate[8] = a8;
|
||||||
|
qstate[9] = a9;
|
||||||
|
qstate[10] = a10;
|
||||||
|
qstate[11] = a11;
|
||||||
|
qstate[12] = a12;
|
||||||
|
qstate[13] = a13;
|
||||||
|
qstate[14] = a14;
|
||||||
|
qstate[15] = a15;
|
||||||
|
qstate[16] = a16;
|
||||||
|
qstate[17] = a17;
|
||||||
|
qstate[18] = a18;
|
||||||
|
qstate[19] = a19;
|
||||||
|
qstate[20] = a20;
|
||||||
|
qstate[21] = a21;
|
||||||
|
qstate[22] = a22;
|
||||||
|
qstate[23] = a23;
|
||||||
|
qstate[24] = a24;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates 64-bit number x by n bits
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl(int x, int n) => (x << n) ^ (x >>> (64 - n));
|
||||||
|
}
|
||||||
119
hashlib/lib/src/algorithms/md2.dart
Normal file
119
hashlib/lib/src/algorithms/md2.dart
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
/// Permutation of 0..255 constructed from the digits of pi. It gives a
|
||||||
|
/// "random" nonlinear byte substitution operation.
|
||||||
|
const _pi = <int>[
|
||||||
|
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, //
|
||||||
|
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
|
||||||
|
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
|
||||||
|
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
|
||||||
|
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
|
||||||
|
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
|
||||||
|
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
|
||||||
|
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
|
||||||
|
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
|
||||||
|
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
|
||||||
|
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
|
||||||
|
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
|
||||||
|
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
|
||||||
|
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
|
||||||
|
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
|
||||||
|
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
|
||||||
|
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
|
||||||
|
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
|
||||||
|
];
|
||||||
|
|
||||||
|
const _padding = <List<int>>[
|
||||||
|
[],
|
||||||
|
[01],
|
||||||
|
[02, 02],
|
||||||
|
[03, 03, 03],
|
||||||
|
[04, 04, 04, 04],
|
||||||
|
[05, 05, 05, 05, 05],
|
||||||
|
[06, 06, 06, 06, 06, 06],
|
||||||
|
[07, 07, 07, 07, 07, 07, 07],
|
||||||
|
[08, 08, 08, 08, 08, 08, 08, 08],
|
||||||
|
[09, 09, 09, 09, 09, 09, 09, 09, 09],
|
||||||
|
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
|
||||||
|
[11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11],
|
||||||
|
[12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
|
||||||
|
[13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13],
|
||||||
|
[14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14],
|
||||||
|
[15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15],
|
||||||
|
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the RSA Laboratories'
|
||||||
|
/// [The MD2 Message-Digest Algorithm][rfc1319].
|
||||||
|
///
|
||||||
|
/// [rfc1319]: https://www.ietf.org/rfc/rfc1319.html
|
||||||
|
class MD2Hash extends BlockHashSink {
|
||||||
|
final state = Uint8List(48);
|
||||||
|
final checksum = Uint8List(16);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
MD2Hash()
|
||||||
|
: hashLength = 128 >>> 3,
|
||||||
|
super(16);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.fillRange(0, 16, 0);
|
||||||
|
checksum.fillRange(0, 16, 0);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update(List<int> block, [int offset = 0, bool last = false]) {
|
||||||
|
int i, j, t;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
state[16 + i] = block[i];
|
||||||
|
state[32 + i] = block[i] ^ state[i];
|
||||||
|
}
|
||||||
|
t = 0;
|
||||||
|
for (i = 0; i < 18; i++) {
|
||||||
|
for (j = 0; j < 48; ++j) {
|
||||||
|
t = state[j] ^= _pi[t];
|
||||||
|
}
|
||||||
|
t = (t + i) & 0xFF;
|
||||||
|
}
|
||||||
|
t = checksum[15];
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
t = checksum[i] ^= _pi[block[i] ^ t];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// add padding
|
||||||
|
$process(_padding[blockLength - pos], 0, blockLength - pos);
|
||||||
|
|
||||||
|
// process checksum
|
||||||
|
$update(checksum);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return state.sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
142
hashlib/lib/src/algorithms/md4.dart
Normal file
142
hashlib/lib/src/algorithms/md4.dart
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x67452301, // a
|
||||||
|
0xEFCDAB89, // b
|
||||||
|
0x98BADCFE, // c
|
||||||
|
0x10325476, // d
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Shift constants
|
||||||
|
const _rc = <int>[
|
||||||
|
03, 07, 11, 19, 03, 07, 11, 19, 03, 07, 11, 19, 03, 07, 11, 19, //
|
||||||
|
03, 05, 09, 13, 03, 05, 09, 13, 03, 05, 09, 13, 03, 05, 09, 13, //
|
||||||
|
03, 09, 11, 15, 03, 09, 11, 15, 03, 09, 11, 15, 03, 09, 11, 15,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the RSA Data Security, Inc.
|
||||||
|
/// [MD4 Message-Digest Algorithm][rfc1320].
|
||||||
|
///
|
||||||
|
/// [rfc1320]: https://www.ietf.org/rfc/rfc1320.html
|
||||||
|
class MD4Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
MD4Hash()
|
||||||
|
: state = Uint32List.fromList(_iv),
|
||||||
|
hashLength = 128 >>> 3,
|
||||||
|
super(512 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int a, b, c, d, e, f, g, h, t;
|
||||||
|
var x = sbuffer;
|
||||||
|
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
e = (b & c) | ((~b & _mask32) & d);
|
||||||
|
f = i;
|
||||||
|
t = d;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
g = (a + e + x[f]) & _mask32;
|
||||||
|
h = _rc[i];
|
||||||
|
b = ((g << h) & _mask32) | (g >>> (32 - h));
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 16; i < 32; i++) {
|
||||||
|
e = (b & c) | (b & d) | (c & d);
|
||||||
|
f = ((i >>> 2) & 3) + ((i & 3) << 2);
|
||||||
|
t = d;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
g = (a + e + 0x5a827999 + x[f]) & _mask32;
|
||||||
|
h = _rc[i];
|
||||||
|
b = ((g << h) & _mask32) | (g >>> (32 - h));
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 32; i < 48; i++) {
|
||||||
|
e = b ^ c ^ d;
|
||||||
|
f = ((i & 1) << 3) | ((i & 2) << 1) | ((i >>> 1) & 2) | ((i >>> 3) & 1);
|
||||||
|
t = d;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
g = (a + e + 0x6ed9eba1 + x[f]) & _mask32;
|
||||||
|
h = _rc[i];
|
||||||
|
b = ((g << h) & _mask32) | (g >>> (32 - h));
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
bdata.setUint32(56, messageLengthInBits, Endian.little);
|
||||||
|
bdata.setUint32(60, messageLengthInBits >>> 32, Endian.little);
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update();
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
175
hashlib/lib/src/algorithms/md5.dart
Normal file
175
hashlib/lib/src/algorithms/md5.dart
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x67452301, // a
|
||||||
|
0xEFCDAB89, // b
|
||||||
|
0x98BADCFE, // c
|
||||||
|
0x10325476, // d
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 64 constants [Formula: floor(2^32 * abs(sin(i + 1)))]
|
||||||
|
const _k = <int>[
|
||||||
|
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //
|
||||||
|
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||||
|
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||||
|
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||||
|
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||||
|
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||||
|
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||||
|
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||||
|
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||||
|
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||||
|
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||||
|
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||||
|
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||||
|
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||||
|
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||||
|
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Shift constants
|
||||||
|
const _rc = <int>[
|
||||||
|
07, 12, 17, 22, 07, 12, 17, 22, 07, 12, 17, 22, 07, 12, 17, 22, //
|
||||||
|
05, 09, 14, 20, 05, 09, 14, 20, 05, 09, 14, 20, 05, 09, 14, 20, //
|
||||||
|
04, 11, 16, 23, 04, 11, 16, 23, 04, 11, 16, 23, 04, 11, 16, 23, //
|
||||||
|
06, 10, 15, 21, 06, 10, 15, 21, 06, 10, 15, 21, 06, 10, 15, 21,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the RSA Data Security, Inc.
|
||||||
|
/// [MD5 Message-Digest Algorithm][rfc1321].
|
||||||
|
///
|
||||||
|
/// [rfc1321]: https://www.ietf.org/rfc/rfc1321.html
|
||||||
|
class MD5Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
MD5Hash()
|
||||||
|
: state = Uint32List.fromList(_iv),
|
||||||
|
hashLength = 128 >>> 3,
|
||||||
|
super(512 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int a, b, c, d, e, f, g, h, t;
|
||||||
|
var x = sbuffer;
|
||||||
|
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
e = (b & c) | ((~b & _mask32) & d);
|
||||||
|
f = i;
|
||||||
|
t = d;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
g = (a + e + _k[i] + x[f]) & _mask32;
|
||||||
|
h = _rc[i];
|
||||||
|
b += ((g << h) & _mask32) | (g >>> (32 - h));
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 16; i < 32; i++) {
|
||||||
|
e = (d & b) | ((~d & _mask32) & c);
|
||||||
|
f = ((i << 2) + i + 1) & 15;
|
||||||
|
t = d;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
g = (a + e + _k[i] + x[f]) & _mask32;
|
||||||
|
h = _rc[i];
|
||||||
|
b += ((g << h) & _mask32) | (g >>> (32 - h));
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 32; i < 48; i++) {
|
||||||
|
e = b ^ c ^ d;
|
||||||
|
f = ((i << 1) + i + 5) & 15;
|
||||||
|
t = d;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
g = (a + e + _k[i] + x[f]) & _mask32;
|
||||||
|
h = _rc[i];
|
||||||
|
b += ((g << h) & _mask32) | (g >>> (32 - h));
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 48; i < 64; i++) {
|
||||||
|
e = c ^ (b | (~d & _mask32));
|
||||||
|
f = ((i << 3) - i) & 15;
|
||||||
|
t = d;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
g = (a + e + _k[i] + x[f]) & _mask32;
|
||||||
|
h = _rc[i];
|
||||||
|
b += ((g << h) & _mask32) | (g >>> (32 - h));
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
bdata.setUint32(56, messageLengthInBits, Endian.little);
|
||||||
|
bdata.setUint32(60, messageLengthInBits >>> 32, Endian.little);
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update();
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
137
hashlib/lib/src/algorithms/pbkdf2/pbkdf2.dart
Normal file
137
hashlib/lib/src/algorithms/pbkdf2/pbkdf2.dart
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_digest.dart';
|
||||||
|
import 'package:hashlib/src/core/kdf_base.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
import 'package:hashlib/src/random.dart';
|
||||||
|
|
||||||
|
import 'security.dart';
|
||||||
|
|
||||||
|
int _defaultKeyLength = 24;
|
||||||
|
int _defaultSaltLength = 16;
|
||||||
|
|
||||||
|
/// This is an implementation of Password Based Key Derivation Algorithm,
|
||||||
|
/// PBKDF2 derived from [RFC-8081][rfc], which internally uses a MAC based
|
||||||
|
/// Pseudo Random Function (PRF) for key derivation.
|
||||||
|
///
|
||||||
|
/// PBKDF2 is part of Public-Key Cryptography Standards (PKCS) series published
|
||||||
|
/// by the RSA Laboratories, specifically PKCS #5 v2.0. It supersedes PBKDF1,
|
||||||
|
/// which could only produce derived keys up to 160 bits long.
|
||||||
|
///
|
||||||
|
/// The strength of the generated password using PBKDF2 depends on the number
|
||||||
|
/// of iterations. The idea is to prevent a brute force attack on the original
|
||||||
|
/// password by making the key derivation time long. This implementation can be
|
||||||
|
/// used for both to [convert] a passphrase and [verify] it with a derived key.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.rfc-editor.org/rfc/rfc8018.html#section-5.2
|
||||||
|
class PBKDF2 extends KeyDerivatorBase {
|
||||||
|
@override
|
||||||
|
String get name => '${algo.name}/PBKDF2';
|
||||||
|
|
||||||
|
/// The underlying algorithm used as Pseudo Random Function (PRF)
|
||||||
|
final MACHash algo;
|
||||||
|
|
||||||
|
/// The byte array containing salt
|
||||||
|
final List<int> salt;
|
||||||
|
|
||||||
|
/// The number of iterations
|
||||||
|
final int iterations;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength;
|
||||||
|
|
||||||
|
const PBKDF2._({
|
||||||
|
required this.algo,
|
||||||
|
required this.salt,
|
||||||
|
required this.iterations,
|
||||||
|
required this.derivedKeyLength,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Create a [PBKDF2] instance with a MAC instance.
|
||||||
|
factory PBKDF2(
|
||||||
|
MACHash mac,
|
||||||
|
int iterations, {
|
||||||
|
List<int>? salt,
|
||||||
|
int? keyLength,
|
||||||
|
}) {
|
||||||
|
keyLength ??= _defaultKeyLength;
|
||||||
|
salt ??= randomBytes(_defaultSaltLength);
|
||||||
|
|
||||||
|
// validate parameters
|
||||||
|
if (iterations < 1) {
|
||||||
|
throw StateError('The iterations must be at least 1');
|
||||||
|
}
|
||||||
|
if (iterations > 0x7FFFFFFF) {
|
||||||
|
throw StateError('The iterations must be less than 2^31');
|
||||||
|
}
|
||||||
|
if (keyLength < 1) {
|
||||||
|
throw StateError('The keyLength must be at least 1');
|
||||||
|
}
|
||||||
|
|
||||||
|
// create instance
|
||||||
|
return PBKDF2._(
|
||||||
|
algo: mac,
|
||||||
|
salt: salt,
|
||||||
|
iterations: iterations,
|
||||||
|
derivedKeyLength: keyLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [PBKDF2] instance from [PBKDF2Security].
|
||||||
|
factory PBKDF2.fromSecurity(
|
||||||
|
PBKDF2Security security, {
|
||||||
|
List<int>? salt,
|
||||||
|
MACHash? mac,
|
||||||
|
int? iterations,
|
||||||
|
int? keyLength,
|
||||||
|
}) =>
|
||||||
|
PBKDF2(
|
||||||
|
mac ?? security.mac,
|
||||||
|
iterations ?? security.c,
|
||||||
|
keyLength: keyLength ?? security.dklen,
|
||||||
|
salt: salt,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
HashDigest convert(List<int> password) {
|
||||||
|
int i, j, k, t;
|
||||||
|
Uint8List hash, block;
|
||||||
|
var result = Uint8List(derivedKeyLength);
|
||||||
|
|
||||||
|
// Initialize the MAC with provided password
|
||||||
|
var sink = algo.by(password).createSink();
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
for (i = 1; k < derivedKeyLength; i++) {
|
||||||
|
// Generate the first HMAC: U_1
|
||||||
|
sink.reset();
|
||||||
|
sink.add(salt);
|
||||||
|
sink.add([i >>> 24, i >>> 16, i >>> 8, i]);
|
||||||
|
hash = sink.digest().bytes;
|
||||||
|
|
||||||
|
// For storing the combined XORs
|
||||||
|
block = hash;
|
||||||
|
|
||||||
|
// Subsequence HMAC generation: U_2 .. U_c
|
||||||
|
for (t = 1; t < iterations; ++t) {
|
||||||
|
sink.reset();
|
||||||
|
sink.add(hash);
|
||||||
|
hash = sink.digest().bytes;
|
||||||
|
|
||||||
|
for (j = 0; j < hash.length; ++j) {
|
||||||
|
block[j] ^= hash[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the hash to the result
|
||||||
|
for (j = 0; j < hash.length && k < derivedKeyLength; ++j, ++k) {
|
||||||
|
result[k] = block[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HashDigest(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
hashlib/lib/src/algorithms/pbkdf2/security.dart
Normal file
90
hashlib/lib/src/algorithms/pbkdf2/security.dart
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
import 'package:hashlib/src/hmac.dart';
|
||||||
|
import 'package:hashlib/src/md5.dart';
|
||||||
|
import 'package:hashlib/src/sha1.dart';
|
||||||
|
import 'package:hashlib/src/sha256.dart';
|
||||||
|
import 'package:hashlib/src/sha3_256.dart';
|
||||||
|
import 'package:hashlib/src/sha512.dart';
|
||||||
|
|
||||||
|
import 'pbkdf2.dart';
|
||||||
|
|
||||||
|
/// This contains some recommended parameters for [PBKDF2] algorithm.
|
||||||
|
class PBKDF2Security {
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// The number of iterations
|
||||||
|
final int c;
|
||||||
|
|
||||||
|
/// The length of the derived key
|
||||||
|
final int dklen;
|
||||||
|
|
||||||
|
/// The underlying algorithm
|
||||||
|
final MACHash mac;
|
||||||
|
|
||||||
|
const PBKDF2Security(
|
||||||
|
this.name, {
|
||||||
|
required this.c,
|
||||||
|
required this.mac,
|
||||||
|
required this.dklen,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Provides a very low security. Use it only for test purposes.
|
||||||
|
///
|
||||||
|
/// It uses MD5/HMAC algorithm with a cost of 10.
|
||||||
|
///
|
||||||
|
/// **WARNING: Not recommended for general use.**
|
||||||
|
static const test = PBKDF2Security('test', mac: HMAC(md5), c: 10, dklen: 16);
|
||||||
|
|
||||||
|
/// Provides low security. Can be used on low-end devices.
|
||||||
|
///
|
||||||
|
/// It uses SHA3-256/HMAC algorithm with 100 iterations.
|
||||||
|
///
|
||||||
|
/// **WARNING: Not recommended for general use.**
|
||||||
|
static const little =
|
||||||
|
PBKDF2Security('little', mac: HMAC(sha3_256), c: 100, dklen: 32);
|
||||||
|
|
||||||
|
/// Provides moderate security.
|
||||||
|
///
|
||||||
|
/// It uses SHA-256/HMAC algorithm with 3,000 iterations.
|
||||||
|
static const moderate =
|
||||||
|
PBKDF2Security('moderate', mac: hmac_sha256, c: 3000, dklen: 32);
|
||||||
|
|
||||||
|
/// Provides good security.
|
||||||
|
///
|
||||||
|
/// It uses SHA-256/HMAC algorithm with 50,000 iterations.
|
||||||
|
static const good =
|
||||||
|
PBKDF2Security('good', mac: hmac_sha256, c: 50000, dklen: 64);
|
||||||
|
|
||||||
|
/// Provides strong security. It uses similar parameters as [owasp2].
|
||||||
|
///
|
||||||
|
/// It uses SHA-256/HMAC algorithm with 600,000 iterations.
|
||||||
|
static const strong =
|
||||||
|
PBKDF2Security('strong', mac: HMAC(sha256), c: 600000, dklen: 64);
|
||||||
|
|
||||||
|
/// Provides strong security recommended by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses SHA1/HMAC algorithm with 1,300,000 iterations.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp =
|
||||||
|
PBKDF2Security('owasp1', mac: HMAC(sha1), c: 1300000, dklen: 32);
|
||||||
|
|
||||||
|
/// Provides strong security recommended by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses SHA-256/HMAC algorithm with 600,000 iterations.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp2 =
|
||||||
|
PBKDF2Security('owasp2', mac: HMAC(sha256), c: 600000, dklen: 64);
|
||||||
|
|
||||||
|
/// Provides strong security recommended by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses SHA-512/HMAC algorithm with 210,000 iterations.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp3 =
|
||||||
|
PBKDF2Security('owasp3', mac: HMAC(sha512), c: 210000, dklen: 64);
|
||||||
|
}
|
||||||
105
hashlib/lib/src/algorithms/poly1305/poly1305_32bit.dart
Normal file
105
hashlib/lib/src/algorithms/poly1305/poly1305_32bit.dart
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
const List<int> _clamp = <int>[
|
||||||
|
0xff, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, //
|
||||||
|
0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the [The Poly1305 Algorithms] described
|
||||||
|
/// in the [ChaCha20 and Poly1305 for IETF Protocols][rfc8439] document.
|
||||||
|
///
|
||||||
|
/// [rfc8439]: https://www.ietf.org/rfc/rfc8439.html
|
||||||
|
class Poly1305Sink extends BlockHashSink implements MACSinkBase {
|
||||||
|
BigInt _n = BigInt.zero;
|
||||||
|
BigInt _r = BigInt.zero;
|
||||||
|
BigInt _s = BigInt.zero;
|
||||||
|
BigInt _h = BigInt.zero;
|
||||||
|
final BigInt _m = BigInt.two.pow(128);
|
||||||
|
final BigInt _p = BigInt.two.pow(130) - BigInt.from(5);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 16;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength = 16;
|
||||||
|
|
||||||
|
/// Creates a new instance to process 16-bytes blocks with 17-bytes buffer
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [key] : The key-pair (`r`, `s`) - 16 or 32-bytes.
|
||||||
|
Poly1305Sink(Uint8List key) : super(16, bufferLength: 17) {
|
||||||
|
if (key.length != 16 && key.length != 32) {
|
||||||
|
throw ArgumentError('The key length must be either 16 or 32 bytes');
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
_r = BigInt.zero;
|
||||||
|
for (i = 15; i >= 0; i--) {
|
||||||
|
_r <<= 8;
|
||||||
|
_r += BigInt.from(key[i] & _clamp[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.length == 32) {
|
||||||
|
_s = BigInt.zero;
|
||||||
|
for (i = 31; i >= 16; i--) {
|
||||||
|
_s <<= 8;
|
||||||
|
_s += BigInt.from(key[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_n = BigInt.zero;
|
||||||
|
_h = BigInt.zero;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
_n += BigInt.one << 128;
|
||||||
|
$update();
|
||||||
|
_n = BigInt.zero;
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
_n += BigInt.from(chunk[start]) << (pos << 3);
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
_n += BigInt.one << 128;
|
||||||
|
$update();
|
||||||
|
_n = BigInt.zero;
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
_h = ((_h + _n) * _r) % _p;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
if (pos > 0) {
|
||||||
|
_n += BigInt.one << (pos << 3);
|
||||||
|
$update();
|
||||||
|
}
|
||||||
|
|
||||||
|
_h += _s;
|
||||||
|
|
||||||
|
var result = Uint32List.fromList([
|
||||||
|
(_h % _m).toUnsigned(32).toInt(),
|
||||||
|
((_h >> 32) % _m).toUnsigned(32).toInt(),
|
||||||
|
((_h >> 64) % _m).toUnsigned(32).toInt(),
|
||||||
|
((_h >> 96) % _m).toUnsigned(32).toInt(),
|
||||||
|
]);
|
||||||
|
return Uint8List.view(result.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
220
hashlib/lib/src/algorithms/poly1305/poly1305_64bit.dart
Normal file
220
hashlib/lib/src/algorithms/poly1305/poly1305_64bit.dart
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
const int _mask26 = 0x03FFFFFF;
|
||||||
|
|
||||||
|
/// This implementation is derived from the [The Poly1305 Algorithms][pdf]
|
||||||
|
/// described in the [ChaCha20 and Poly1305 for IETF Protocols][rfc] document.
|
||||||
|
///
|
||||||
|
/// The Reference implementation used for optimization:
|
||||||
|
/// https://github.com/floodyberry/poly1305-opt
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc8439.html
|
||||||
|
/// [pdf]: https://cr.yp.to/mac/poly1305-20050329.pdf
|
||||||
|
class Poly1305Sink extends BlockHashSink implements MACSinkBase {
|
||||||
|
// secret key: r
|
||||||
|
int _r0 = 0;
|
||||||
|
int _r1 = 0;
|
||||||
|
int _r2 = 0;
|
||||||
|
int _r3 = 0;
|
||||||
|
int _r4 = 0;
|
||||||
|
// authentication key: s
|
||||||
|
int _s0 = 0;
|
||||||
|
int _s1 = 0;
|
||||||
|
int _s2 = 0;
|
||||||
|
int _s3 = 0;
|
||||||
|
// accumulator: a
|
||||||
|
int _h0 = 0;
|
||||||
|
int _h1 = 0;
|
||||||
|
int _h2 = 0;
|
||||||
|
int _h3 = 0;
|
||||||
|
int _h4 = 0;
|
||||||
|
// g = 5 * r
|
||||||
|
int _g1 = 0;
|
||||||
|
int _g2 = 0;
|
||||||
|
int _g3 = 0;
|
||||||
|
int _g4 = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 16;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength = 16;
|
||||||
|
|
||||||
|
/// Creates a new instance to process 16-bytes blocks with 17-bytes buffer
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [key] : The key-pair (`r`, `s`) - 16 or 32-bytes.
|
||||||
|
Poly1305Sink(Uint8List key) : super(16, bufferLength: 17) {
|
||||||
|
if (key.length != 16 && key.length != 32) {
|
||||||
|
throw ArgumentError('The key length must be either 16 or 32 bytes');
|
||||||
|
}
|
||||||
|
|
||||||
|
// r = key[15..0]
|
||||||
|
_r0 = key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24);
|
||||||
|
_r1 = (key[3] >>> 2) | (key[4] << 6) | (key[5] << 14) | (key[6] << 22);
|
||||||
|
_r2 = (key[6] >>> 4) | (key[7] << 4) | (key[8] << 12) | (key[9] << 20);
|
||||||
|
_r3 = (key[9] >>> 6) | (key[10] << 2) | (key[11] << 10) | (key[12] << 18);
|
||||||
|
_r4 = key[13] | (key[14] << 8) | (key[15] << 16);
|
||||||
|
|
||||||
|
// clamp(r): r &= 0x0ffffffc0ffffffc0ffffffc0fffffff
|
||||||
|
_r0 &= 0x03ffffff;
|
||||||
|
_r1 &= 0x03ffff03;
|
||||||
|
_r2 &= 0x03ffc0ff;
|
||||||
|
_r3 &= 0x03f03fff;
|
||||||
|
_r4 &= 0x000fffff;
|
||||||
|
|
||||||
|
_g1 = 5 * _r1;
|
||||||
|
_g2 = 5 * _r2;
|
||||||
|
_g3 = 5 * _r3;
|
||||||
|
_g4 = 5 * _r4;
|
||||||
|
|
||||||
|
if (key.length == 32) {
|
||||||
|
// s = key[31..16]
|
||||||
|
_s0 = key[16] | (key[17] << 8) | (key[18] << 16) | (key[19] << 24);
|
||||||
|
_s1 = key[20] | (key[21] << 8) | (key[22] << 16) | (key[23] << 24);
|
||||||
|
_s2 = key[24] | (key[25] << 8) | (key[26] << 16) | (key[27] << 24);
|
||||||
|
_s3 = key[28] | (key[29] << 8) | (key[30] << 16) | (key[31] << 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_h0 = 0;
|
||||||
|
_h1 = 0;
|
||||||
|
_h2 = 0;
|
||||||
|
_h3 = 0;
|
||||||
|
_h4 = 0;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
buffer[16] = 1;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int d0, d1, d2, d3, d4;
|
||||||
|
|
||||||
|
// a += n
|
||||||
|
_h0 += buffer[0] |
|
||||||
|
(buffer[1] << 8) |
|
||||||
|
(buffer[2] << 16) |
|
||||||
|
((buffer[3] & 0x03) << 24);
|
||||||
|
_h1 += (buffer[3] >>> 2) |
|
||||||
|
(buffer[4] << 6) |
|
||||||
|
(buffer[5] << 14) |
|
||||||
|
((buffer[6] & 0xF) << 22);
|
||||||
|
_h2 += (buffer[6] >>> 4) |
|
||||||
|
(buffer[7] << 4) |
|
||||||
|
(buffer[8] << 12) |
|
||||||
|
((buffer[9] & 0x3F) << 20);
|
||||||
|
_h3 += (buffer[9] >>> 6) |
|
||||||
|
(buffer[10] << 2) |
|
||||||
|
(buffer[11] << 10) |
|
||||||
|
(buffer[12] << 18);
|
||||||
|
_h4 += buffer[13] |
|
||||||
|
(buffer[14] << 8) |
|
||||||
|
(buffer[15] << 16) |
|
||||||
|
((buffer[16] & 0x03) << 24);
|
||||||
|
|
||||||
|
// a *= r
|
||||||
|
d0 = _h0 * _r0 + _h1 * _g4 + _h2 * _g3 + _h3 * _g2 + _h4 * _g1;
|
||||||
|
d1 = _h0 * _r1 + _h1 * _r0 + _h2 * _g4 + _h3 * _g3 + _h4 * _g2;
|
||||||
|
d2 = _h0 * _r2 + _h1 * _r1 + _h2 * _r0 + _h3 * _g4 + _h4 * _g3;
|
||||||
|
d3 = _h0 * _r3 + _h1 * _r2 + _h2 * _r1 + _h3 * _r0 + _h4 * _g4;
|
||||||
|
d4 = _h0 * _r4 + _h1 * _r3 + _h2 * _r2 + _h3 * _r1 + _h4 * _r0;
|
||||||
|
|
||||||
|
// a %= 2^130 - 5;
|
||||||
|
d1 += d0 >>> 26;
|
||||||
|
d2 += d1 >>> 26;
|
||||||
|
d3 += d2 >>> 26;
|
||||||
|
d4 += d3 >>> 26;
|
||||||
|
_h0 = d0 & _mask26;
|
||||||
|
_h1 = d1 & _mask26;
|
||||||
|
_h2 = d2 & _mask26;
|
||||||
|
_h3 = d3 & _mask26;
|
||||||
|
_h4 = d4 & _mask26;
|
||||||
|
_h0 += 5 * (d4 >>> 26);
|
||||||
|
_h1 += _h0 >>> 26;
|
||||||
|
_h0 &= _mask26;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
if (pos > 0) {
|
||||||
|
buffer[pos] = 1;
|
||||||
|
for (pos++; pos <= 16; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
}
|
||||||
|
|
||||||
|
int d0, d1, d2, d3, d4;
|
||||||
|
|
||||||
|
// fully carry
|
||||||
|
_h1 += _h0 >>> 26;
|
||||||
|
_h2 += _h1 >>> 26;
|
||||||
|
_h3 += _h2 >>> 26;
|
||||||
|
_h4 += _h3 >>> 26;
|
||||||
|
_h0 &= _mask26;
|
||||||
|
_h1 &= _mask26;
|
||||||
|
_h2 &= _mask26;
|
||||||
|
_h3 &= _mask26;
|
||||||
|
|
||||||
|
// compute d = h - p
|
||||||
|
d0 = _h0 + 5;
|
||||||
|
d1 = _h1 + (d0 >>> 26);
|
||||||
|
d2 = _h2 + (d1 >>> 26);
|
||||||
|
d3 = _h3 + (d2 >>> 26);
|
||||||
|
d4 = _h4 + (d3 >>> 26) - (1 << 26);
|
||||||
|
d4 &= _mask32;
|
||||||
|
|
||||||
|
// if h < p, take h; else, take d
|
||||||
|
if ((d4 >>> 31) != 1) {
|
||||||
|
_h0 = d0 & _mask26;
|
||||||
|
_h1 = d1 & _mask26;
|
||||||
|
_h2 = d2 & _mask26;
|
||||||
|
_h3 = d3 & _mask26;
|
||||||
|
_h4 = d4 & _mask26;
|
||||||
|
}
|
||||||
|
|
||||||
|
// modulus 2^128
|
||||||
|
_h0 = ((_h0) | (_h1 << 26)) & _mask32;
|
||||||
|
_h1 = ((_h1 >>> 6) | (_h2 << 20)) & _mask32;
|
||||||
|
_h2 = ((_h2 >>> 12) | (_h3 << 14)) & _mask32;
|
||||||
|
_h3 = ((_h3 >>> 18) | (_h4 << 8)) & _mask32;
|
||||||
|
|
||||||
|
// h += s
|
||||||
|
_h0 += _s0;
|
||||||
|
_h1 += _s1 + (_h0 >>> 32);
|
||||||
|
_h2 += _s2 + (_h1 >>> 32);
|
||||||
|
_h3 += _s3 + (_h2 >>> 32);
|
||||||
|
|
||||||
|
var result = Uint32List.fromList([
|
||||||
|
_h0,
|
||||||
|
_h1,
|
||||||
|
_h2,
|
||||||
|
_h3,
|
||||||
|
]);
|
||||||
|
return Uint8List.view(result.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
hashlib/lib/src/algorithms/poly1305/poly1305_sink.dart
Normal file
4
hashlib/lib/src/algorithms/poly1305/poly1305_sink.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
export 'poly1305_64bit.dart' if (dart.library.js) 'poly1305_32bit.dart';
|
||||||
328
hashlib/lib/src/algorithms/ripemd/ripemd128.dart
Normal file
328
hashlib/lib/src/algorithms/ripemd/ripemd128.dart
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x67452301,
|
||||||
|
0xefcdab89,
|
||||||
|
0x98badcfe,
|
||||||
|
0x10325476,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the Bouncy Castle's implementation of
|
||||||
|
/// [RIPEMD-128][bc].
|
||||||
|
///
|
||||||
|
/// [bc]: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD128Digest.java
|
||||||
|
class RIPEMD128Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
RIPEMD128Hash()
|
||||||
|
: state = Uint32List.fromList(_iv),
|
||||||
|
hashLength = 128 >>> 3,
|
||||||
|
super(512 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f1(int x, int y, int z) => x ^ y ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f2(int x, int y, int z) => (x & y) | (((~x) & _mask32) & z);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f3(int x, int y, int z) => (x | ((~y) & _mask32)) ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f4(int x, int y, int z) => (x & z) | (y & ((~z) & _mask32));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr1(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f1(b, c, d) + x, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr2(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f2(b, c, d) + x + 0x5a827999, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr3(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f3(b, c, d) + x + 0x6ed9eba1, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr4(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f4(b, c, d) + x + 0x8f1bbcdc, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr1(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f1(b, c, d) + x, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr2(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f2(b, c, d) + x + 0x6d703ef3, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr3(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f3(b, c, d) + x + 0x5c4dd124, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr4(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f4(b, c, d) + x + 0x50a28be6, s);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int a, b, c, d, aa, bb, cc, dd;
|
||||||
|
int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||||
|
|
||||||
|
a = aa = state[0];
|
||||||
|
b = bb = state[1];
|
||||||
|
c = cc = state[2];
|
||||||
|
d = dd = state[3];
|
||||||
|
|
||||||
|
x0 = sbuffer[0];
|
||||||
|
x1 = sbuffer[1];
|
||||||
|
x2 = sbuffer[2];
|
||||||
|
x3 = sbuffer[3];
|
||||||
|
x4 = sbuffer[4];
|
||||||
|
x5 = sbuffer[5];
|
||||||
|
x6 = sbuffer[6];
|
||||||
|
x7 = sbuffer[7];
|
||||||
|
x8 = sbuffer[8];
|
||||||
|
x9 = sbuffer[9];
|
||||||
|
x10 = sbuffer[10];
|
||||||
|
x11 = sbuffer[11];
|
||||||
|
x12 = sbuffer[12];
|
||||||
|
x13 = sbuffer[13];
|
||||||
|
x14 = sbuffer[14];
|
||||||
|
x15 = sbuffer[15];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 1
|
||||||
|
//
|
||||||
|
a = _lr1(a, b, c, d, x0, 11);
|
||||||
|
d = _lr1(d, a, b, c, x1, 14);
|
||||||
|
c = _lr1(c, d, a, b, x2, 15);
|
||||||
|
b = _lr1(b, c, d, a, x3, 12);
|
||||||
|
a = _lr1(a, b, c, d, x4, 5);
|
||||||
|
d = _lr1(d, a, b, c, x5, 8);
|
||||||
|
c = _lr1(c, d, a, b, x6, 7);
|
||||||
|
b = _lr1(b, c, d, a, x7, 9);
|
||||||
|
a = _lr1(a, b, c, d, x8, 11);
|
||||||
|
d = _lr1(d, a, b, c, x9, 13);
|
||||||
|
c = _lr1(c, d, a, b, x10, 14);
|
||||||
|
b = _lr1(b, c, d, a, x11, 15);
|
||||||
|
a = _lr1(a, b, c, d, x12, 6);
|
||||||
|
d = _lr1(d, a, b, c, x13, 7);
|
||||||
|
c = _lr1(c, d, a, b, x14, 9);
|
||||||
|
b = _lr1(b, c, d, a, x15, 8);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 2
|
||||||
|
//
|
||||||
|
a = _lr2(a, b, c, d, x7, 7);
|
||||||
|
d = _lr2(d, a, b, c, x4, 6);
|
||||||
|
c = _lr2(c, d, a, b, x13, 8);
|
||||||
|
b = _lr2(b, c, d, a, x1, 13);
|
||||||
|
a = _lr2(a, b, c, d, x10, 11);
|
||||||
|
d = _lr2(d, a, b, c, x6, 9);
|
||||||
|
c = _lr2(c, d, a, b, x15, 7);
|
||||||
|
b = _lr2(b, c, d, a, x3, 15);
|
||||||
|
a = _lr2(a, b, c, d, x12, 7);
|
||||||
|
d = _lr2(d, a, b, c, x0, 12);
|
||||||
|
c = _lr2(c, d, a, b, x9, 15);
|
||||||
|
b = _lr2(b, c, d, a, x5, 9);
|
||||||
|
a = _lr2(a, b, c, d, x2, 11);
|
||||||
|
d = _lr2(d, a, b, c, x14, 7);
|
||||||
|
c = _lr2(c, d, a, b, x11, 13);
|
||||||
|
b = _lr2(b, c, d, a, x8, 12);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 3
|
||||||
|
//
|
||||||
|
a = _lr3(a, b, c, d, x3, 11);
|
||||||
|
d = _lr3(d, a, b, c, x10, 13);
|
||||||
|
c = _lr3(c, d, a, b, x14, 6);
|
||||||
|
b = _lr3(b, c, d, a, x4, 7);
|
||||||
|
a = _lr3(a, b, c, d, x9, 14);
|
||||||
|
d = _lr3(d, a, b, c, x15, 9);
|
||||||
|
c = _lr3(c, d, a, b, x8, 13);
|
||||||
|
b = _lr3(b, c, d, a, x1, 15);
|
||||||
|
a = _lr3(a, b, c, d, x2, 14);
|
||||||
|
d = _lr3(d, a, b, c, x7, 8);
|
||||||
|
c = _lr3(c, d, a, b, x0, 13);
|
||||||
|
b = _lr3(b, c, d, a, x6, 6);
|
||||||
|
a = _lr3(a, b, c, d, x13, 5);
|
||||||
|
d = _lr3(d, a, b, c, x11, 12);
|
||||||
|
c = _lr3(c, d, a, b, x5, 7);
|
||||||
|
b = _lr3(b, c, d, a, x12, 5);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 4
|
||||||
|
//
|
||||||
|
a = _lr4(a, b, c, d, x1, 11);
|
||||||
|
d = _lr4(d, a, b, c, x9, 12);
|
||||||
|
c = _lr4(c, d, a, b, x11, 14);
|
||||||
|
b = _lr4(b, c, d, a, x10, 15);
|
||||||
|
a = _lr4(a, b, c, d, x0, 14);
|
||||||
|
d = _lr4(d, a, b, c, x8, 15);
|
||||||
|
c = _lr4(c, d, a, b, x12, 9);
|
||||||
|
b = _lr4(b, c, d, a, x4, 8);
|
||||||
|
a = _lr4(a, b, c, d, x13, 9);
|
||||||
|
d = _lr4(d, a, b, c, x3, 14);
|
||||||
|
c = _lr4(c, d, a, b, x7, 5);
|
||||||
|
b = _lr4(b, c, d, a, x15, 6);
|
||||||
|
a = _lr4(a, b, c, d, x14, 8);
|
||||||
|
d = _lr4(d, a, b, c, x5, 6);
|
||||||
|
c = _lr4(c, d, a, b, x6, 5);
|
||||||
|
b = _lr4(b, c, d, a, x2, 12);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parallel round 1
|
||||||
|
//
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x5, 8);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x14, 9);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x7, 9);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x0, 11);
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x9, 13);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x2, 15);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x11, 15);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x4, 5);
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x13, 7);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x6, 7);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x15, 8);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x8, 11);
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x1, 14);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x10, 14);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x3, 12);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x12, 6);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parallel round 2
|
||||||
|
//
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x6, 9);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x11, 13);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x3, 15);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x7, 7);
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x0, 12);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x13, 8);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x5, 9);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x10, 11);
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x14, 7);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x15, 7);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x8, 12);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x12, 7);
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x4, 6);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x9, 15);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x1, 13);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x2, 11);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parallel round 3
|
||||||
|
//
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x15, 9);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x5, 7);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x1, 15);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x3, 11);
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x7, 8);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x14, 6);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x6, 6);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x9, 14);
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x11, 12);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x8, 13);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x12, 5);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x2, 14);
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x10, 13);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x0, 13);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x4, 7);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x13, 5);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parallel round 4
|
||||||
|
//
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x8, 15);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x6, 5);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x4, 8);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x1, 11);
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x3, 14);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x11, 14);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x15, 6);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x0, 14);
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x5, 6);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x12, 9);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x2, 12);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x13, 9);
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x9, 12);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x7, 5);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x10, 15);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x14, 8);
|
||||||
|
|
||||||
|
//
|
||||||
|
// combine the results
|
||||||
|
//
|
||||||
|
dd += c + state[1];
|
||||||
|
state[1] = state[2] + d + aa;
|
||||||
|
state[2] = state[3] + a + bb;
|
||||||
|
state[3] = state[0] + b + cc;
|
||||||
|
state[0] = dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
bdata.setUint32(56, messageLengthInBits, Endian.little);
|
||||||
|
bdata.setUint32(60, messageLengthInBits >>> 32, Endian.little);
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update();
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
494
hashlib/lib/src/algorithms/ripemd/ripemd160.dart
Normal file
494
hashlib/lib/src/algorithms/ripemd/ripemd160.dart
Normal file
|
|
@ -0,0 +1,494 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x67452301,
|
||||||
|
0xefcdab89,
|
||||||
|
0x98badcfe,
|
||||||
|
0x10325476,
|
||||||
|
0xc3d2e1f0,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the Bouncy Castle's implementation of
|
||||||
|
/// [RIPEMD-160][bc].
|
||||||
|
///
|
||||||
|
/// [bc]: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD160Digest.java
|
||||||
|
class RIPEMD160Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
RIPEMD160Hash()
|
||||||
|
: state = Uint32List.fromList(_iv),
|
||||||
|
hashLength = 160 >>> 3,
|
||||||
|
super(512 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f1(int x, int y, int z) => x ^ y ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f2(int x, int y, int z) => (x & y) | (((~x) & _mask32) & z);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f3(int x, int y, int z) => (x | ((~y) & _mask32)) ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f4(int x, int y, int z) => (x & z) | (y & ((~z) & _mask32));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f5(int x, int y, int z) => x ^ (y | ((~z) & _mask32));
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int a, b, c, d, e, aa, bb, cc, dd, ee;
|
||||||
|
int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||||
|
|
||||||
|
a = aa = state[0];
|
||||||
|
b = bb = state[1];
|
||||||
|
c = cc = state[2];
|
||||||
|
d = dd = state[3];
|
||||||
|
e = ee = state[4];
|
||||||
|
|
||||||
|
x0 = sbuffer[0];
|
||||||
|
x1 = sbuffer[1];
|
||||||
|
x2 = sbuffer[2];
|
||||||
|
x3 = sbuffer[3];
|
||||||
|
x4 = sbuffer[4];
|
||||||
|
x5 = sbuffer[5];
|
||||||
|
x6 = sbuffer[6];
|
||||||
|
x7 = sbuffer[7];
|
||||||
|
x8 = sbuffer[8];
|
||||||
|
x9 = sbuffer[9];
|
||||||
|
x10 = sbuffer[10];
|
||||||
|
x11 = sbuffer[11];
|
||||||
|
x12 = sbuffer[12];
|
||||||
|
x13 = sbuffer[13];
|
||||||
|
x14 = sbuffer[14];
|
||||||
|
x15 = sbuffer[15];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 1 - 16
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x0, 11) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f1(a, b, c) + x1, 14) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f1(e, a, b) + x2, 15) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f1(d, e, a) + x3, 12) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f1(c, d, e) + x4, 5) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x5, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f1(a, b, c) + x6, 7) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f1(e, a, b) + x7, 9) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f1(d, e, a) + x8, 11) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f1(c, d, e) + x9, 13) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x10, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f1(a, b, c) + x11, 15) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f1(e, a, b) + x12, 6) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f1(d, e, a) + x13, 7) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f1(c, d, e) + x14, 9) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x15, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x5 + 0x50a28be6, 8) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f5(aa, bb, cc) + x14 + 0x50a28be6, 9) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f5(ee, aa, bb) + x7 + 0x50a28be6, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f5(dd, ee, aa) + x0 + 0x50a28be6, 11) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f5(cc, dd, ee) + x9 + 0x50a28be6, 13) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x2 + 0x50a28be6, 15) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f5(aa, bb, cc) + x11 + 0x50a28be6, 15) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f5(ee, aa, bb) + x4 + 0x50a28be6, 5) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f5(dd, ee, aa) + x13 + 0x50a28be6, 7) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f5(cc, dd, ee) + x6 + 0x50a28be6, 7) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x15 + 0x50a28be6, 8) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f5(aa, bb, cc) + x8 + 0x50a28be6, 11) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f5(ee, aa, bb) + x1 + 0x50a28be6, 14) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f5(dd, ee, aa) + x10 + 0x50a28be6, 14) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f5(cc, dd, ee) + x3 + 0x50a28be6, 12) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x12 + 0x50a28be6, 6) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 16-31
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x7 + 0x5a827999, 7) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f2(e, a, b) + x4 + 0x5a827999, 6) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f2(d, e, a) + x13 + 0x5a827999, 8) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f2(c, d, e) + x1 + 0x5a827999, 13) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f2(b, c, d) + x10 + 0x5a827999, 11) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x6 + 0x5a827999, 9) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f2(e, a, b) + x15 + 0x5a827999, 7) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f2(d, e, a) + x3 + 0x5a827999, 15) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f2(c, d, e) + x12 + 0x5a827999, 7) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f2(b, c, d) + x0 + 0x5a827999, 12) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x9 + 0x5a827999, 15) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f2(e, a, b) + x5 + 0x5a827999, 9) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f2(d, e, a) + x2 + 0x5a827999, 11) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f2(c, d, e) + x14 + 0x5a827999, 7) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f2(b, c, d) + x11 + 0x5a827999, 13) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x8 + 0x5a827999, 12) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x6 + 0x5c4dd124, 9) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f4(ee, aa, bb) + x11 + 0x5c4dd124, 13) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f4(dd, ee, aa) + x3 + 0x5c4dd124, 15) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f4(cc, dd, ee) + x7 + 0x5c4dd124, 7) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f4(bb, cc, dd) + x0 + 0x5c4dd124, 12) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x13 + 0x5c4dd124, 8) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f4(ee, aa, bb) + x5 + 0x5c4dd124, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f4(dd, ee, aa) + x10 + 0x5c4dd124, 11) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f4(cc, dd, ee) + x14 + 0x5c4dd124, 7) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f4(bb, cc, dd) + x15 + 0x5c4dd124, 7) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x8 + 0x5c4dd124, 12) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f4(ee, aa, bb) + x12 + 0x5c4dd124, 7) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f4(dd, ee, aa) + x4 + 0x5c4dd124, 6) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f4(cc, dd, ee) + x9 + 0x5c4dd124, 15) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f4(bb, cc, dd) + x1 + 0x5c4dd124, 13) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x2 + 0x5c4dd124, 11) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 32-47
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x3 + 0x6ed9eba1, 11) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f3(d, e, a) + x10 + 0x6ed9eba1, 13) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f3(c, d, e) + x14 + 0x6ed9eba1, 6) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f3(b, c, d) + x4 + 0x6ed9eba1, 7) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f3(a, b, c) + x9 + 0x6ed9eba1, 14) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x15 + 0x6ed9eba1, 9) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f3(d, e, a) + x8 + 0x6ed9eba1, 13) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f3(c, d, e) + x1 + 0x6ed9eba1, 15) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f3(b, c, d) + x2 + 0x6ed9eba1, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f3(a, b, c) + x7 + 0x6ed9eba1, 8) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x0 + 0x6ed9eba1, 13) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f3(d, e, a) + x6 + 0x6ed9eba1, 6) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f3(c, d, e) + x13 + 0x6ed9eba1, 5) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f3(b, c, d) + x11 + 0x6ed9eba1, 12) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f3(a, b, c) + x5 + 0x6ed9eba1, 7) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x12 + 0x6ed9eba1, 5) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x15 + 0x6d703ef3, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f3(dd, ee, aa) + x5 + 0x6d703ef3, 7) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f3(cc, dd, ee) + x1 + 0x6d703ef3, 15) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f3(bb, cc, dd) + x3 + 0x6d703ef3, 11) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f3(aa, bb, cc) + x7 + 0x6d703ef3, 8) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x14 + 0x6d703ef3, 6) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f3(dd, ee, aa) + x6 + 0x6d703ef3, 6) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f3(cc, dd, ee) + x9 + 0x6d703ef3, 14) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f3(bb, cc, dd) + x11 + 0x6d703ef3, 12) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f3(aa, bb, cc) + x8 + 0x6d703ef3, 13) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x12 + 0x6d703ef3, 5) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f3(dd, ee, aa) + x2 + 0x6d703ef3, 14) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f3(cc, dd, ee) + x10 + 0x6d703ef3, 13) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f3(bb, cc, dd) + x0 + 0x6d703ef3, 13) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f3(aa, bb, cc) + x4 + 0x6d703ef3, 7) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x13 + 0x6d703ef3, 5) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 48-63
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x1 + 0x8f1bbcdc, 11) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f4(c, d, e) + x9 + 0x8f1bbcdc, 12) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f4(b, c, d) + x11 + 0x8f1bbcdc, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f4(a, b, c) + x10 + 0x8f1bbcdc, 15) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f4(e, a, b) + x0 + 0x8f1bbcdc, 14) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x8 + 0x8f1bbcdc, 15) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f4(c, d, e) + x12 + 0x8f1bbcdc, 9) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f4(b, c, d) + x4 + 0x8f1bbcdc, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f4(a, b, c) + x13 + 0x8f1bbcdc, 9) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f4(e, a, b) + x3 + 0x8f1bbcdc, 14) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x7 + 0x8f1bbcdc, 5) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f4(c, d, e) + x15 + 0x8f1bbcdc, 6) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f4(b, c, d) + x14 + 0x8f1bbcdc, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f4(a, b, c) + x5 + 0x8f1bbcdc, 6) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f4(e, a, b) + x6 + 0x8f1bbcdc, 5) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x2 + 0x8f1bbcdc, 12) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x8 + 0x7a6d76e9, 15) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f2(cc, dd, ee) + x6 + 0x7a6d76e9, 5) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f2(bb, cc, dd) + x4 + 0x7a6d76e9, 8) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f2(aa, bb, cc) + x1 + 0x7a6d76e9, 11) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f2(ee, aa, bb) + x3 + 0x7a6d76e9, 14) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x11 + 0x7a6d76e9, 14) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f2(cc, dd, ee) + x15 + 0x7a6d76e9, 6) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f2(bb, cc, dd) + x0 + 0x7a6d76e9, 14) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f2(aa, bb, cc) + x5 + 0x7a6d76e9, 6) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f2(ee, aa, bb) + x12 + 0x7a6d76e9, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x2 + 0x7a6d76e9, 12) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f2(cc, dd, ee) + x13 + 0x7a6d76e9, 9) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f2(bb, cc, dd) + x9 + 0x7a6d76e9, 12) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f2(aa, bb, cc) + x7 + 0x7a6d76e9, 5) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f2(ee, aa, bb) + x10 + 0x7a6d76e9, 15) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x14 + 0x7a6d76e9, 8) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 64-79
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x4 + 0xa953fd4e, 9) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f5(b, c, d) + x0 + 0xa953fd4e, 15) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f5(a, b, c) + x5 + 0xa953fd4e, 5) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f5(e, a, b) + x9 + 0xa953fd4e, 11) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f5(d, e, a) + x7 + 0xa953fd4e, 6) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x12 + 0xa953fd4e, 8) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f5(b, c, d) + x2 + 0xa953fd4e, 13) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f5(a, b, c) + x10 + 0xa953fd4e, 12) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f5(e, a, b) + x14 + 0xa953fd4e, 5) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f5(d, e, a) + x1 + 0xa953fd4e, 12) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x3 + 0xa953fd4e, 13) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f5(b, c, d) + x8 + 0xa953fd4e, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f5(a, b, c) + x11 + 0xa953fd4e, 11) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f5(e, a, b) + x6 + 0xa953fd4e, 8) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f5(d, e, a) + x15 + 0xa953fd4e, 5) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x13 + 0xa953fd4e, 6) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x12, 8) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f1(bb, cc, dd) + x15, 5) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f1(aa, bb, cc) + x10, 12) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f1(ee, aa, bb) + x4, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f1(dd, ee, aa) + x1, 12) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x5, 5) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f1(bb, cc, dd) + x8, 14) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f1(aa, bb, cc) + x7, 6) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f1(ee, aa, bb) + x6, 8) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f1(dd, ee, aa) + x2, 13) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x13, 6) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f1(bb, cc, dd) + x14, 5) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f1(aa, bb, cc) + x0, 15) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f1(ee, aa, bb) + x3, 13) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f1(dd, ee, aa) + x9, 11) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x11, 11) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
|
||||||
|
dd += c + state[1];
|
||||||
|
state[1] = state[2] + d + ee;
|
||||||
|
state[2] = state[3] + e + aa;
|
||||||
|
state[3] = state[4] + a + bb;
|
||||||
|
state[4] = state[0] + b + cc;
|
||||||
|
state[0] = dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
bdata.setUint32(56, messageLengthInBits, Endian.little);
|
||||||
|
bdata.setUint32(60, messageLengthInBits >>> 32, Endian.little);
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update();
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
343
hashlib/lib/src/algorithms/ripemd/ripemd256.dart
Normal file
343
hashlib/lib/src/algorithms/ripemd/ripemd256.dart
Normal file
|
|
@ -0,0 +1,343 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x67452301,
|
||||||
|
0xefcdab89,
|
||||||
|
0x98badcfe,
|
||||||
|
0x10325476,
|
||||||
|
0x76543210,
|
||||||
|
0xFEDCBA98,
|
||||||
|
0x89ABCDEF,
|
||||||
|
0x01234567,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the Bouncy Castle's implementation of
|
||||||
|
/// [RIPEMD-256][bc].
|
||||||
|
///
|
||||||
|
/// [bc]: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD256Digest.java
|
||||||
|
class RIPEMD256Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
RIPEMD256Hash()
|
||||||
|
: state = Uint32List.fromList(_iv),
|
||||||
|
hashLength = 256 >>> 3,
|
||||||
|
super(512 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f1(int x, int y, int z) => x ^ y ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f2(int x, int y, int z) => (x & y) | (((~x) & _mask32) & z);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f3(int x, int y, int z) => (x | ((~y) & _mask32)) ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f4(int x, int y, int z) => (x & z) | (y & ((~z) & _mask32));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr1(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f1(b, c, d) + x, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr2(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f2(b, c, d) + x + 0x5a827999, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr3(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f3(b, c, d) + x + 0x6ed9eba1, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _lr4(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f4(b, c, d) + x + 0x8f1bbcdc, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr1(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f1(b, c, d) + x, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr2(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f2(b, c, d) + x + 0x6d703ef3, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr3(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f3(b, c, d) + x + 0x5c4dd124, s);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rr4(int a, int b, int c, int d, int x, int s) =>
|
||||||
|
_rotl32(a + _f4(b, c, d) + x + 0x50a28be6, s);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int a, b, c, d, aa, bb, cc, dd, t;
|
||||||
|
int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||||
|
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
aa = state[4];
|
||||||
|
bb = state[5];
|
||||||
|
cc = state[6];
|
||||||
|
dd = state[7];
|
||||||
|
|
||||||
|
x0 = sbuffer[0];
|
||||||
|
x1 = sbuffer[1];
|
||||||
|
x2 = sbuffer[2];
|
||||||
|
x3 = sbuffer[3];
|
||||||
|
x4 = sbuffer[4];
|
||||||
|
x5 = sbuffer[5];
|
||||||
|
x6 = sbuffer[6];
|
||||||
|
x7 = sbuffer[7];
|
||||||
|
x8 = sbuffer[8];
|
||||||
|
x9 = sbuffer[9];
|
||||||
|
x10 = sbuffer[10];
|
||||||
|
x11 = sbuffer[11];
|
||||||
|
x12 = sbuffer[12];
|
||||||
|
x13 = sbuffer[13];
|
||||||
|
x14 = sbuffer[14];
|
||||||
|
x15 = sbuffer[15];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 1
|
||||||
|
//
|
||||||
|
a = _lr1(a, b, c, d, x0, 11);
|
||||||
|
d = _lr1(d, a, b, c, x1, 14);
|
||||||
|
c = _lr1(c, d, a, b, x2, 15);
|
||||||
|
b = _lr1(b, c, d, a, x3, 12);
|
||||||
|
a = _lr1(a, b, c, d, x4, 5);
|
||||||
|
d = _lr1(d, a, b, c, x5, 8);
|
||||||
|
c = _lr1(c, d, a, b, x6, 7);
|
||||||
|
b = _lr1(b, c, d, a, x7, 9);
|
||||||
|
a = _lr1(a, b, c, d, x8, 11);
|
||||||
|
d = _lr1(d, a, b, c, x9, 13);
|
||||||
|
c = _lr1(c, d, a, b, x10, 14);
|
||||||
|
b = _lr1(b, c, d, a, x11, 15);
|
||||||
|
a = _lr1(a, b, c, d, x12, 6);
|
||||||
|
d = _lr1(d, a, b, c, x13, 7);
|
||||||
|
c = _lr1(c, d, a, b, x14, 9);
|
||||||
|
b = _lr1(b, c, d, a, x15, 8);
|
||||||
|
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x5, 8);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x14, 9);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x7, 9);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x0, 11);
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x9, 13);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x2, 15);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x11, 15);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x4, 5);
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x13, 7);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x6, 7);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x15, 8);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x8, 11);
|
||||||
|
aa = _rr4(aa, bb, cc, dd, x1, 14);
|
||||||
|
dd = _rr4(dd, aa, bb, cc, x10, 14);
|
||||||
|
cc = _rr4(cc, dd, aa, bb, x3, 12);
|
||||||
|
bb = _rr4(bb, cc, dd, aa, x12, 6);
|
||||||
|
|
||||||
|
t = a;
|
||||||
|
a = aa;
|
||||||
|
aa = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 2
|
||||||
|
//
|
||||||
|
a = _lr2(a, b, c, d, x7, 7);
|
||||||
|
d = _lr2(d, a, b, c, x4, 6);
|
||||||
|
c = _lr2(c, d, a, b, x13, 8);
|
||||||
|
b = _lr2(b, c, d, a, x1, 13);
|
||||||
|
a = _lr2(a, b, c, d, x10, 11);
|
||||||
|
d = _lr2(d, a, b, c, x6, 9);
|
||||||
|
c = _lr2(c, d, a, b, x15, 7);
|
||||||
|
b = _lr2(b, c, d, a, x3, 15);
|
||||||
|
a = _lr2(a, b, c, d, x12, 7);
|
||||||
|
d = _lr2(d, a, b, c, x0, 12);
|
||||||
|
c = _lr2(c, d, a, b, x9, 15);
|
||||||
|
b = _lr2(b, c, d, a, x5, 9);
|
||||||
|
a = _lr2(a, b, c, d, x2, 11);
|
||||||
|
d = _lr2(d, a, b, c, x14, 7);
|
||||||
|
c = _lr2(c, d, a, b, x11, 13);
|
||||||
|
b = _lr2(b, c, d, a, x8, 12);
|
||||||
|
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x6, 9);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x11, 13);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x3, 15);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x7, 7);
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x0, 12);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x13, 8);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x5, 9);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x10, 11);
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x14, 7);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x15, 7);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x8, 12);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x12, 7);
|
||||||
|
aa = _rr3(aa, bb, cc, dd, x4, 6);
|
||||||
|
dd = _rr3(dd, aa, bb, cc, x9, 15);
|
||||||
|
cc = _rr3(cc, dd, aa, bb, x1, 13);
|
||||||
|
bb = _rr3(bb, cc, dd, aa, x2, 11);
|
||||||
|
|
||||||
|
t = b;
|
||||||
|
b = bb;
|
||||||
|
bb = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 3
|
||||||
|
//
|
||||||
|
a = _lr3(a, b, c, d, x3, 11);
|
||||||
|
d = _lr3(d, a, b, c, x10, 13);
|
||||||
|
c = _lr3(c, d, a, b, x14, 6);
|
||||||
|
b = _lr3(b, c, d, a, x4, 7);
|
||||||
|
a = _lr3(a, b, c, d, x9, 14);
|
||||||
|
d = _lr3(d, a, b, c, x15, 9);
|
||||||
|
c = _lr3(c, d, a, b, x8, 13);
|
||||||
|
b = _lr3(b, c, d, a, x1, 15);
|
||||||
|
a = _lr3(a, b, c, d, x2, 14);
|
||||||
|
d = _lr3(d, a, b, c, x7, 8);
|
||||||
|
c = _lr3(c, d, a, b, x0, 13);
|
||||||
|
b = _lr3(b, c, d, a, x6, 6);
|
||||||
|
a = _lr3(a, b, c, d, x13, 5);
|
||||||
|
d = _lr3(d, a, b, c, x11, 12);
|
||||||
|
c = _lr3(c, d, a, b, x5, 7);
|
||||||
|
b = _lr3(b, c, d, a, x12, 5);
|
||||||
|
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x15, 9);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x5, 7);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x1, 15);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x3, 11);
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x7, 8);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x14, 6);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x6, 6);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x9, 14);
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x11, 12);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x8, 13);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x12, 5);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x2, 14);
|
||||||
|
aa = _rr2(aa, bb, cc, dd, x10, 13);
|
||||||
|
dd = _rr2(dd, aa, bb, cc, x0, 13);
|
||||||
|
cc = _rr2(cc, dd, aa, bb, x4, 7);
|
||||||
|
bb = _rr2(bb, cc, dd, aa, x13, 5);
|
||||||
|
|
||||||
|
t = c;
|
||||||
|
c = cc;
|
||||||
|
cc = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Round 4
|
||||||
|
//
|
||||||
|
a = _lr4(a, b, c, d, x1, 11);
|
||||||
|
d = _lr4(d, a, b, c, x9, 12);
|
||||||
|
c = _lr4(c, d, a, b, x11, 14);
|
||||||
|
b = _lr4(b, c, d, a, x10, 15);
|
||||||
|
a = _lr4(a, b, c, d, x0, 14);
|
||||||
|
d = _lr4(d, a, b, c, x8, 15);
|
||||||
|
c = _lr4(c, d, a, b, x12, 9);
|
||||||
|
b = _lr4(b, c, d, a, x4, 8);
|
||||||
|
a = _lr4(a, b, c, d, x13, 9);
|
||||||
|
d = _lr4(d, a, b, c, x3, 14);
|
||||||
|
c = _lr4(c, d, a, b, x7, 5);
|
||||||
|
b = _lr4(b, c, d, a, x15, 6);
|
||||||
|
a = _lr4(a, b, c, d, x14, 8);
|
||||||
|
d = _lr4(d, a, b, c, x5, 6);
|
||||||
|
c = _lr4(c, d, a, b, x6, 5);
|
||||||
|
b = _lr4(b, c, d, a, x2, 12);
|
||||||
|
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x8, 15);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x6, 5);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x4, 8);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x1, 11);
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x3, 14);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x11, 14);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x15, 6);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x0, 14);
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x5, 6);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x12, 9);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x2, 12);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x13, 9);
|
||||||
|
aa = _rr1(aa, bb, cc, dd, x9, 12);
|
||||||
|
dd = _rr1(dd, aa, bb, cc, x7, 5);
|
||||||
|
cc = _rr1(cc, dd, aa, bb, x10, 15);
|
||||||
|
bb = _rr1(bb, cc, dd, aa, x14, 8);
|
||||||
|
|
||||||
|
t = d;
|
||||||
|
d = dd;
|
||||||
|
dd = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// combine the results
|
||||||
|
//
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += aa;
|
||||||
|
state[5] += bb;
|
||||||
|
state[6] += cc;
|
||||||
|
state[7] += dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
bdata.setUint32(56, messageLengthInBits, Endian.little);
|
||||||
|
bdata.setUint32(60, messageLengthInBits >>> 32, Endian.little);
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update();
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
527
hashlib/lib/src/algorithms/ripemd/ripemd320.dart
Normal file
527
hashlib/lib/src/algorithms/ripemd/ripemd320.dart
Normal file
|
|
@ -0,0 +1,527 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x67452301,
|
||||||
|
0xefcdab89,
|
||||||
|
0x98badcfe,
|
||||||
|
0x10325476,
|
||||||
|
0xc3d2e1f0,
|
||||||
|
0x76543210,
|
||||||
|
0xFEDCBA98,
|
||||||
|
0x89ABCDEF,
|
||||||
|
0x01234567,
|
||||||
|
0x3C2D1E0F,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the Bouncy Castle's implementation of
|
||||||
|
/// [RIPEMD-320][bc].
|
||||||
|
///
|
||||||
|
/// [bc]: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/RIPEMD320Digest.java
|
||||||
|
class RIPEMD320Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
RIPEMD320Hash()
|
||||||
|
: state = Uint32List.fromList(_iv),
|
||||||
|
hashLength = 320 >>> 3,
|
||||||
|
super(512 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f1(int x, int y, int z) => x ^ y ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f2(int x, int y, int z) => (x & y) | (((~x) & _mask32) & z);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f3(int x, int y, int z) => (x | ((~y) & _mask32)) ^ z;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f4(int x, int y, int z) => (x & z) | (y & ((~z) & _mask32));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _f5(int x, int y, int z) => x ^ (y | ((~z) & _mask32));
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int a, b, c, d, e, aa, bb, cc, dd, ee, t;
|
||||||
|
int x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||||
|
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
aa = state[5];
|
||||||
|
bb = state[6];
|
||||||
|
cc = state[7];
|
||||||
|
dd = state[8];
|
||||||
|
ee = state[9];
|
||||||
|
|
||||||
|
x0 = sbuffer[0];
|
||||||
|
x1 = sbuffer[1];
|
||||||
|
x2 = sbuffer[2];
|
||||||
|
x3 = sbuffer[3];
|
||||||
|
x4 = sbuffer[4];
|
||||||
|
x5 = sbuffer[5];
|
||||||
|
x6 = sbuffer[6];
|
||||||
|
x7 = sbuffer[7];
|
||||||
|
x8 = sbuffer[8];
|
||||||
|
x9 = sbuffer[9];
|
||||||
|
x10 = sbuffer[10];
|
||||||
|
x11 = sbuffer[11];
|
||||||
|
x12 = sbuffer[12];
|
||||||
|
x13 = sbuffer[13];
|
||||||
|
x14 = sbuffer[14];
|
||||||
|
x15 = sbuffer[15];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 1 - 16
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x0, 11) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f1(a, b, c) + x1, 14) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f1(e, a, b) + x2, 15) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f1(d, e, a) + x3, 12) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f1(c, d, e) + x4, 5) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x5, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f1(a, b, c) + x6, 7) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f1(e, a, b) + x7, 9) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f1(d, e, a) + x8, 11) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f1(c, d, e) + x9, 13) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x10, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f1(a, b, c) + x11, 15) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f1(e, a, b) + x12, 6) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f1(d, e, a) + x13, 7) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f1(c, d, e) + x14, 9) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f1(b, c, d) + x15, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x5 + 0x50a28be6, 8) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f5(aa, bb, cc) + x14 + 0x50a28be6, 9) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f5(ee, aa, bb) + x7 + 0x50a28be6, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f5(dd, ee, aa) + x0 + 0x50a28be6, 11) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f5(cc, dd, ee) + x9 + 0x50a28be6, 13) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x2 + 0x50a28be6, 15) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f5(aa, bb, cc) + x11 + 0x50a28be6, 15) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f5(ee, aa, bb) + x4 + 0x50a28be6, 5) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f5(dd, ee, aa) + x13 + 0x50a28be6, 7) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f5(cc, dd, ee) + x6 + 0x50a28be6, 7) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x15 + 0x50a28be6, 8) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f5(aa, bb, cc) + x8 + 0x50a28be6, 11) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f5(ee, aa, bb) + x1 + 0x50a28be6, 14) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f5(dd, ee, aa) + x10 + 0x50a28be6, 14) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f5(cc, dd, ee) + x3 + 0x50a28be6, 12) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f5(bb, cc, dd) + x12 + 0x50a28be6, 6) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
|
||||||
|
t = a;
|
||||||
|
a = aa;
|
||||||
|
aa = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 16-31
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x7 + 0x5a827999, 7) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f2(e, a, b) + x4 + 0x5a827999, 6) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f2(d, e, a) + x13 + 0x5a827999, 8) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f2(c, d, e) + x1 + 0x5a827999, 13) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f2(b, c, d) + x10 + 0x5a827999, 11) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x6 + 0x5a827999, 9) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f2(e, a, b) + x15 + 0x5a827999, 7) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f2(d, e, a) + x3 + 0x5a827999, 15) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f2(c, d, e) + x12 + 0x5a827999, 7) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f2(b, c, d) + x0 + 0x5a827999, 12) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x9 + 0x5a827999, 15) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f2(e, a, b) + x5 + 0x5a827999, 9) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f2(d, e, a) + x2 + 0x5a827999, 11) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f2(c, d, e) + x14 + 0x5a827999, 7) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f2(b, c, d) + x11 + 0x5a827999, 13) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f2(a, b, c) + x8 + 0x5a827999, 12) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x6 + 0x5c4dd124, 9) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f4(ee, aa, bb) + x11 + 0x5c4dd124, 13) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f4(dd, ee, aa) + x3 + 0x5c4dd124, 15) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f4(cc, dd, ee) + x7 + 0x5c4dd124, 7) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f4(bb, cc, dd) + x0 + 0x5c4dd124, 12) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x13 + 0x5c4dd124, 8) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f4(ee, aa, bb) + x5 + 0x5c4dd124, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f4(dd, ee, aa) + x10 + 0x5c4dd124, 11) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f4(cc, dd, ee) + x14 + 0x5c4dd124, 7) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f4(bb, cc, dd) + x15 + 0x5c4dd124, 7) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x8 + 0x5c4dd124, 12) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f4(ee, aa, bb) + x12 + 0x5c4dd124, 7) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f4(dd, ee, aa) + x4 + 0x5c4dd124, 6) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f4(cc, dd, ee) + x9 + 0x5c4dd124, 15) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f4(bb, cc, dd) + x1 + 0x5c4dd124, 13) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f4(aa, bb, cc) + x2 + 0x5c4dd124, 11) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
|
||||||
|
t = b;
|
||||||
|
b = bb;
|
||||||
|
bb = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 32-47
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x3 + 0x6ed9eba1, 11) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f3(d, e, a) + x10 + 0x6ed9eba1, 13) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f3(c, d, e) + x14 + 0x6ed9eba1, 6) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f3(b, c, d) + x4 + 0x6ed9eba1, 7) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f3(a, b, c) + x9 + 0x6ed9eba1, 14) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x15 + 0x6ed9eba1, 9) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f3(d, e, a) + x8 + 0x6ed9eba1, 13) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f3(c, d, e) + x1 + 0x6ed9eba1, 15) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f3(b, c, d) + x2 + 0x6ed9eba1, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f3(a, b, c) + x7 + 0x6ed9eba1, 8) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x0 + 0x6ed9eba1, 13) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f3(d, e, a) + x6 + 0x6ed9eba1, 6) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f3(c, d, e) + x13 + 0x6ed9eba1, 5) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f3(b, c, d) + x11 + 0x6ed9eba1, 12) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f3(a, b, c) + x5 + 0x6ed9eba1, 7) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f3(e, a, b) + x12 + 0x6ed9eba1, 5) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x15 + 0x6d703ef3, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f3(dd, ee, aa) + x5 + 0x6d703ef3, 7) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f3(cc, dd, ee) + x1 + 0x6d703ef3, 15) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f3(bb, cc, dd) + x3 + 0x6d703ef3, 11) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f3(aa, bb, cc) + x7 + 0x6d703ef3, 8) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x14 + 0x6d703ef3, 6) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f3(dd, ee, aa) + x6 + 0x6d703ef3, 6) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f3(cc, dd, ee) + x9 + 0x6d703ef3, 14) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f3(bb, cc, dd) + x11 + 0x6d703ef3, 12) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f3(aa, bb, cc) + x8 + 0x6d703ef3, 13) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x12 + 0x6d703ef3, 5) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f3(dd, ee, aa) + x2 + 0x6d703ef3, 14) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f3(cc, dd, ee) + x10 + 0x6d703ef3, 13) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f3(bb, cc, dd) + x0 + 0x6d703ef3, 13) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f3(aa, bb, cc) + x4 + 0x6d703ef3, 7) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f3(ee, aa, bb) + x13 + 0x6d703ef3, 5) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
|
||||||
|
t = c;
|
||||||
|
c = cc;
|
||||||
|
cc = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 48-63
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x1 + 0x8f1bbcdc, 11) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f4(c, d, e) + x9 + 0x8f1bbcdc, 12) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f4(b, c, d) + x11 + 0x8f1bbcdc, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f4(a, b, c) + x10 + 0x8f1bbcdc, 15) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f4(e, a, b) + x0 + 0x8f1bbcdc, 14) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x8 + 0x8f1bbcdc, 15) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f4(c, d, e) + x12 + 0x8f1bbcdc, 9) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f4(b, c, d) + x4 + 0x8f1bbcdc, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f4(a, b, c) + x13 + 0x8f1bbcdc, 9) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f4(e, a, b) + x3 + 0x8f1bbcdc, 14) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x7 + 0x8f1bbcdc, 5) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f4(c, d, e) + x15 + 0x8f1bbcdc, 6) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f4(b, c, d) + x14 + 0x8f1bbcdc, 8) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f4(a, b, c) + x5 + 0x8f1bbcdc, 6) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f4(e, a, b) + x6 + 0x8f1bbcdc, 5) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f4(d, e, a) + x2 + 0x8f1bbcdc, 12) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x8 + 0x7a6d76e9, 15) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f2(cc, dd, ee) + x6 + 0x7a6d76e9, 5) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f2(bb, cc, dd) + x4 + 0x7a6d76e9, 8) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f2(aa, bb, cc) + x1 + 0x7a6d76e9, 11) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f2(ee, aa, bb) + x3 + 0x7a6d76e9, 14) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x11 + 0x7a6d76e9, 14) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f2(cc, dd, ee) + x15 + 0x7a6d76e9, 6) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f2(bb, cc, dd) + x0 + 0x7a6d76e9, 14) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f2(aa, bb, cc) + x5 + 0x7a6d76e9, 6) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f2(ee, aa, bb) + x12 + 0x7a6d76e9, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x2 + 0x7a6d76e9, 12) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f2(cc, dd, ee) + x13 + 0x7a6d76e9, 9) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f2(bb, cc, dd) + x9 + 0x7a6d76e9, 12) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f2(aa, bb, cc) + x7 + 0x7a6d76e9, 5) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f2(ee, aa, bb) + x10 + 0x7a6d76e9, 15) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f2(dd, ee, aa) + x14 + 0x7a6d76e9, 8) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
|
||||||
|
t = d;
|
||||||
|
d = dd;
|
||||||
|
dd = t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rounds 64-79
|
||||||
|
//
|
||||||
|
// left
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x4 + 0xa953fd4e, 9) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f5(b, c, d) + x0 + 0xa953fd4e, 15) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f5(a, b, c) + x5 + 0xa953fd4e, 5) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f5(e, a, b) + x9 + 0xa953fd4e, 11) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f5(d, e, a) + x7 + 0xa953fd4e, 6) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x12 + 0xa953fd4e, 8) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f5(b, c, d) + x2 + 0xa953fd4e, 13) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f5(a, b, c) + x10 + 0xa953fd4e, 12) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f5(e, a, b) + x14 + 0xa953fd4e, 5) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f5(d, e, a) + x1 + 0xa953fd4e, 12) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x3 + 0xa953fd4e, 13) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
a = _rotl32(a + _f5(b, c, d) + x8 + 0xa953fd4e, 14) + e;
|
||||||
|
c = _rotl32(c, 10);
|
||||||
|
e = _rotl32(e + _f5(a, b, c) + x11 + 0xa953fd4e, 11) + d;
|
||||||
|
b = _rotl32(b, 10);
|
||||||
|
d = _rotl32(d + _f5(e, a, b) + x6 + 0xa953fd4e, 8) + c;
|
||||||
|
a = _rotl32(a, 10);
|
||||||
|
c = _rotl32(c + _f5(d, e, a) + x15 + 0xa953fd4e, 5) + b;
|
||||||
|
e = _rotl32(e, 10);
|
||||||
|
b = _rotl32(b + _f5(c, d, e) + x13 + 0xa953fd4e, 6) + a;
|
||||||
|
d = _rotl32(d, 10);
|
||||||
|
|
||||||
|
// right
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x12, 8) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f1(bb, cc, dd) + x15, 5) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f1(aa, bb, cc) + x10, 12) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f1(ee, aa, bb) + x4, 9) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f1(dd, ee, aa) + x1, 12) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x5, 5) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f1(bb, cc, dd) + x8, 14) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f1(aa, bb, cc) + x7, 6) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f1(ee, aa, bb) + x6, 8) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f1(dd, ee, aa) + x2, 13) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x13, 6) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
aa = _rotl32(aa + _f1(bb, cc, dd) + x14, 5) + ee;
|
||||||
|
cc = _rotl32(cc, 10);
|
||||||
|
ee = _rotl32(ee + _f1(aa, bb, cc) + x0, 15) + dd;
|
||||||
|
bb = _rotl32(bb, 10);
|
||||||
|
dd = _rotl32(dd + _f1(ee, aa, bb) + x3, 13) + cc;
|
||||||
|
aa = _rotl32(aa, 10);
|
||||||
|
cc = _rotl32(cc + _f1(dd, ee, aa) + x9, 11) + bb;
|
||||||
|
ee = _rotl32(ee, 10);
|
||||||
|
bb = _rotl32(bb + _f1(cc, dd, ee) + x11, 11) + aa;
|
||||||
|
dd = _rotl32(dd, 10);
|
||||||
|
|
||||||
|
//
|
||||||
|
// do (e, ee) swap as part of assignment.
|
||||||
|
//
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += ee;
|
||||||
|
state[5] += aa;
|
||||||
|
state[6] += bb;
|
||||||
|
state[7] += cc;
|
||||||
|
state[8] += dd;
|
||||||
|
state[9] += e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
bdata.setUint32(56, messageLengthInBits, Endian.little);
|
||||||
|
bdata.setUint32(60, messageLengthInBits >>> 32, Endian.little);
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update();
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
return Uint8List.view(state.buffer).sublist(0, hashLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
282
hashlib/lib/src/algorithms/scrypt/scrypt.dart
Normal file
282
hashlib/lib/src/algorithms/scrypt/scrypt.dart
Normal file
|
|
@ -0,0 +1,282 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_digest.dart';
|
||||||
|
import 'package:hashlib/src/core/kdf_base.dart';
|
||||||
|
import 'package:hashlib/src/pbkdf2.dart';
|
||||||
|
import 'package:hashlib/src/random.dart';
|
||||||
|
|
||||||
|
import 'security.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
/// This is an implementation of Password Based Key Derivation Algorithm,
|
||||||
|
/// scrypt derived from [RFC-7914][rfc], which internally uses [PBKDF2].
|
||||||
|
///
|
||||||
|
/// The function derives one or more secret keys from a secret string. It is
|
||||||
|
/// based on memory-hard functions, which offer added protection against
|
||||||
|
/// attacks using custom hardware.
|
||||||
|
///
|
||||||
|
/// The strength of the generated password using scrypt depends on the
|
||||||
|
/// CPU/Memory cost, block size and parallelism parameters. Poor parameter
|
||||||
|
/// choices can be harmful for security; for example, if you tune the
|
||||||
|
/// parameters so that memory use is reduced to small amounts that will affect
|
||||||
|
/// the properties of the algorithm.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.rfc-editor.org/rfc/rfc7914.html
|
||||||
|
class Scrypt extends KeyDerivatorBase {
|
||||||
|
@override
|
||||||
|
final String name = 'Scrypt';
|
||||||
|
|
||||||
|
/// The byte array containing salt
|
||||||
|
final List<int> salt;
|
||||||
|
|
||||||
|
/// CPU/Memory cost parameter (N)
|
||||||
|
final int cost;
|
||||||
|
|
||||||
|
/// Block size parameter (r)
|
||||||
|
final int blockSize;
|
||||||
|
|
||||||
|
/// Parallelization parameter (p)
|
||||||
|
final int parallelism;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int derivedKeyLength;
|
||||||
|
|
||||||
|
const Scrypt._({
|
||||||
|
required this.salt,
|
||||||
|
required this.cost,
|
||||||
|
required this.blockSize,
|
||||||
|
required this.parallelism,
|
||||||
|
required this.derivedKeyLength,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Creates an [Scrypt] instance with a sink for MAC generation.
|
||||||
|
factory Scrypt({
|
||||||
|
List<int>? salt,
|
||||||
|
required int cost,
|
||||||
|
int blockSize = 8,
|
||||||
|
int parallelism = 1,
|
||||||
|
int derivedKeyLength = 64,
|
||||||
|
}) {
|
||||||
|
// validate parameters
|
||||||
|
if (cost < 1) {
|
||||||
|
throw StateError('The cost must be at least 1');
|
||||||
|
}
|
||||||
|
if (cost > 0xFFFFFF) {
|
||||||
|
throw StateError('The cost must be less than 2^24');
|
||||||
|
}
|
||||||
|
if (cost & (cost - 1) != 0) {
|
||||||
|
throw StateError('The cost must be a power of 2');
|
||||||
|
}
|
||||||
|
if (derivedKeyLength < 1) {
|
||||||
|
throw StateError('The derivedKeyLength must be at least 1');
|
||||||
|
}
|
||||||
|
if (blockSize < 1) {
|
||||||
|
throw StateError('The blockSize must be at least 1');
|
||||||
|
}
|
||||||
|
if (parallelism < 1) {
|
||||||
|
throw StateError('The parallelism must be at least 1');
|
||||||
|
}
|
||||||
|
if (blockSize * parallelism > 0x1FFFFFF) {
|
||||||
|
throw StateError('The blockSize * parallelism is too big');
|
||||||
|
}
|
||||||
|
salt ??= randomBytes(16);
|
||||||
|
|
||||||
|
// create instance
|
||||||
|
return Scrypt._(
|
||||||
|
salt: salt,
|
||||||
|
cost: cost,
|
||||||
|
blockSize: blockSize,
|
||||||
|
parallelism: parallelism,
|
||||||
|
derivedKeyLength: derivedKeyLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [Scrypt] instance from [ScryptSecurity] parameter.
|
||||||
|
factory Scrypt.fromSecurity(
|
||||||
|
ScryptSecurity security, {
|
||||||
|
List<int>? salt,
|
||||||
|
int derivedKeyLength = 64,
|
||||||
|
}) {
|
||||||
|
return Scrypt(
|
||||||
|
salt: salt,
|
||||||
|
cost: security.N,
|
||||||
|
blockSize: security.r,
|
||||||
|
parallelism: security.p,
|
||||||
|
derivedKeyLength: derivedKeyLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a derived key using the scrypt algorithm.
|
||||||
|
@override
|
||||||
|
HashDigest convert(List<int> password) {
|
||||||
|
int N = cost;
|
||||||
|
int midRO = (blockSize << 4);
|
||||||
|
int roLength = (blockSize << 7);
|
||||||
|
int roLength32 = (roLength >>> 2);
|
||||||
|
int innerKeyLength = parallelism * roLength;
|
||||||
|
int innerKeyLength32 = parallelism * roLength32;
|
||||||
|
Uint32List inp = Uint32List(roLength32);
|
||||||
|
Uint32List out = Uint32List(roLength32);
|
||||||
|
Uint32List t = Uint32List(16);
|
||||||
|
Uint32List v;
|
||||||
|
|
||||||
|
// Initialize the memory
|
||||||
|
List<Uint32List> acc = List.filled(N, Uint32List(roLength32));
|
||||||
|
for (int i = 1; i < N; ++i) {
|
||||||
|
acc[i] = Uint32List(roLength32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive the inner blocks
|
||||||
|
var inner = pbkdf2(password, salt, 1, innerKeyLength).bytes;
|
||||||
|
var inner32 = Uint32List.view(inner.buffer);
|
||||||
|
|
||||||
|
/// [length] = 128 * r = 2 * 64 * r = 4 * 32 * r bytes
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
void blockMix() {
|
||||||
|
int i, j, p, q;
|
||||||
|
p = 0;
|
||||||
|
q = midRO;
|
||||||
|
for (j = 0; j < 16; j++) {
|
||||||
|
t[j] = inp[roLength32 - 16 + j];
|
||||||
|
}
|
||||||
|
for (i = 0; i < roLength32; i += 32) {
|
||||||
|
// even
|
||||||
|
for (j = 0; j < 16; j++) {
|
||||||
|
t[j] ^= inp[i + j];
|
||||||
|
}
|
||||||
|
_salsa20(t);
|
||||||
|
for (j = 0; j < 16; j++, p++) {
|
||||||
|
out[p] = t[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// odd
|
||||||
|
for (j = 0; j < 16; j++) {
|
||||||
|
t[j] ^= inp[i + j + 16];
|
||||||
|
}
|
||||||
|
_salsa20(t);
|
||||||
|
for (j = 0; j < 16; j++, q++) {
|
||||||
|
out[q] = t[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix the inner blocks to derive the outer salt
|
||||||
|
for (int i, j, k = 0; k < innerKeyLength32; k += roLength32) {
|
||||||
|
/// Number of iterations, [N] is a power of 2
|
||||||
|
/// length of [x] = 128 * r = 2 * 64 * r = 4 * 32 * r bytes
|
||||||
|
for (j = 0; j < roLength32; ++j) {
|
||||||
|
inp[j] = inner32[j + k];
|
||||||
|
}
|
||||||
|
for (i = 0; i < N; ++i) {
|
||||||
|
v = acc[i];
|
||||||
|
for (j = 0; j < roLength32; ++j) {
|
||||||
|
v[j] = inp[j];
|
||||||
|
}
|
||||||
|
blockMix();
|
||||||
|
// swap inp <-> out
|
||||||
|
v = inp;
|
||||||
|
inp = out;
|
||||||
|
out = v;
|
||||||
|
}
|
||||||
|
for (i = 0; i < N; ++i) {
|
||||||
|
v = acc[inp[roLength32 - 16] & (N - 1)];
|
||||||
|
for (j = 0; j < roLength32; ++j) {
|
||||||
|
inp[j] ^= v[j];
|
||||||
|
}
|
||||||
|
blockMix();
|
||||||
|
// swap inp <-> out
|
||||||
|
v = inp;
|
||||||
|
inp = out;
|
||||||
|
out = v;
|
||||||
|
}
|
||||||
|
for (j = 0; j < roLength32; ++j) {
|
||||||
|
inner32[j + k] = inp[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive final blocks with the outer salt
|
||||||
|
return pbkdf2(password, inner, 1, derivedKeyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
/// size of [b] = 4 * 16 = 64 bytes
|
||||||
|
static void _salsa20(Uint32List b) {
|
||||||
|
int i, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||||
|
|
||||||
|
x0 = b[0];
|
||||||
|
x1 = b[1];
|
||||||
|
x2 = b[2];
|
||||||
|
x3 = b[3];
|
||||||
|
x4 = b[4];
|
||||||
|
x5 = b[5];
|
||||||
|
x6 = b[6];
|
||||||
|
x7 = b[7];
|
||||||
|
x8 = b[8];
|
||||||
|
x9 = b[9];
|
||||||
|
x10 = b[10];
|
||||||
|
x11 = b[11];
|
||||||
|
x12 = b[12];
|
||||||
|
x13 = b[13];
|
||||||
|
x14 = b[14];
|
||||||
|
x15 = b[15];
|
||||||
|
|
||||||
|
for (i = 8; i > 0; i -= 2) {
|
||||||
|
x4 ^= _rotl32(x0 + x12, 7);
|
||||||
|
x8 ^= _rotl32(x4 + x0, 9);
|
||||||
|
x12 ^= _rotl32(x8 + x4, 13);
|
||||||
|
x0 ^= _rotl32(x12 + x8, 18);
|
||||||
|
x9 ^= _rotl32(x5 + x1, 7);
|
||||||
|
x13 ^= _rotl32(x9 + x5, 9);
|
||||||
|
x1 ^= _rotl32(x13 + x9, 13);
|
||||||
|
x5 ^= _rotl32(x1 + x13, 18);
|
||||||
|
x14 ^= _rotl32(x10 + x6, 7);
|
||||||
|
x2 ^= _rotl32(x14 + x10, 9);
|
||||||
|
x6 ^= _rotl32(x2 + x14, 13);
|
||||||
|
x10 ^= _rotl32(x6 + x2, 18);
|
||||||
|
x3 ^= _rotl32(x15 + x11, 7);
|
||||||
|
x7 ^= _rotl32(x3 + x15, 9);
|
||||||
|
x11 ^= _rotl32(x7 + x3, 13);
|
||||||
|
x15 ^= _rotl32(x11 + x7, 18);
|
||||||
|
x1 ^= _rotl32(x0 + x3, 7);
|
||||||
|
x2 ^= _rotl32(x1 + x0, 9);
|
||||||
|
x3 ^= _rotl32(x2 + x1, 13);
|
||||||
|
x0 ^= _rotl32(x3 + x2, 18);
|
||||||
|
x6 ^= _rotl32(x5 + x4, 7);
|
||||||
|
x7 ^= _rotl32(x6 + x5, 9);
|
||||||
|
x4 ^= _rotl32(x7 + x6, 13);
|
||||||
|
x5 ^= _rotl32(x4 + x7, 18);
|
||||||
|
x11 ^= _rotl32(x10 + x9, 7);
|
||||||
|
x8 ^= _rotl32(x11 + x10, 9);
|
||||||
|
x9 ^= _rotl32(x8 + x11, 13);
|
||||||
|
x10 ^= _rotl32(x9 + x8, 18);
|
||||||
|
x12 ^= _rotl32(x15 + x14, 7);
|
||||||
|
x13 ^= _rotl32(x12 + x15, 9);
|
||||||
|
x14 ^= _rotl32(x13 + x12, 13);
|
||||||
|
x15 ^= _rotl32(x14 + x13, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
b[0] += x0;
|
||||||
|
b[1] += x1;
|
||||||
|
b[2] += x2;
|
||||||
|
b[3] += x3;
|
||||||
|
b[4] += x4;
|
||||||
|
b[5] += x5;
|
||||||
|
b[6] += x6;
|
||||||
|
b[7] += x7;
|
||||||
|
b[8] += x8;
|
||||||
|
b[9] += x9;
|
||||||
|
b[10] += x10;
|
||||||
|
b[11] += x11;
|
||||||
|
b[12] += x12;
|
||||||
|
b[13] += x13;
|
||||||
|
b[14] += x14;
|
||||||
|
b[15] += x15;
|
||||||
|
}
|
||||||
|
}
|
||||||
95
hashlib/lib/src/algorithms/scrypt/security.dart
Normal file
95
hashlib/lib/src/algorithms/scrypt/security.dart
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'scrypt.dart';
|
||||||
|
|
||||||
|
/// This contains some recommended values of memory, iteration and parallelism
|
||||||
|
/// values for [Scrypt] algorithm.
|
||||||
|
///
|
||||||
|
/// It is best to try out different combinations of these values to achieve the
|
||||||
|
/// desired runtime on a target machine.
|
||||||
|
class ScryptSecurity {
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// The size of a single block in bytes
|
||||||
|
final int r;
|
||||||
|
|
||||||
|
/// The CPU/Memory cost parameter as a power of 2. 1 < [N] < 2^32
|
||||||
|
final int N;
|
||||||
|
|
||||||
|
/// The parallelization parameter. [p] <= (2^32 - 1) / (128 * [r])
|
||||||
|
final int p;
|
||||||
|
|
||||||
|
const ScryptSecurity(
|
||||||
|
this.name, {
|
||||||
|
required this.N,
|
||||||
|
required this.r,
|
||||||
|
required this.p,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Provides a very low security. Use it only for test purposes.
|
||||||
|
///
|
||||||
|
/// It uses cost of 16, block size of 2 and parallelism of 1.
|
||||||
|
///
|
||||||
|
/// **WARNING: Not recommended for general use.**
|
||||||
|
static const test = ScryptSecurity('test', N: 1 << 4, r: 2, p: 1);
|
||||||
|
|
||||||
|
/// Provides low security. Can be used on low-end devices.
|
||||||
|
///
|
||||||
|
/// It uses cost of 256, block size of 4 and parallelism of 2.
|
||||||
|
///
|
||||||
|
/// **WARNING: Not recommended for general use.**
|
||||||
|
static const little = ScryptSecurity('little', N: 1 << 8, r: 4, p: 2);
|
||||||
|
|
||||||
|
/// Provides moderate security.
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^10, block size of 8 and parallelism of 3.
|
||||||
|
static const moderate = ScryptSecurity('moderate', N: 1 << 10, r: 8, p: 3);
|
||||||
|
|
||||||
|
/// Provides good security. The default parameters from [RFC-7914][rfc]
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^14, block size of 8 and parallelism of 1.
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc7914.html
|
||||||
|
static const good = ScryptSecurity('good', N: 1 << 14, r: 8, p: 1);
|
||||||
|
|
||||||
|
/// Provides strong security.
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^18, block size of 16 and parallelism of 2.
|
||||||
|
static const strong = ScryptSecurity('strong', N: 1 << 18, r: 8, p: 2);
|
||||||
|
|
||||||
|
/// Provides strong security recommended by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^17, block size of 8 and parallelism of 1.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp = ScryptSecurity('owasp1', N: 1 << 17, r: 8, p: 1);
|
||||||
|
|
||||||
|
/// The second recommendation by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^16, block size of 8 and parallelism of 2.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp2 = ScryptSecurity('owasp2', N: 1 << 16, r: 8, p: 2);
|
||||||
|
|
||||||
|
/// The third recommendation by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^15, block size of 8 and parallelism of 3.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp3 = ScryptSecurity('owasp3', N: 1 << 15, r: 8, p: 3);
|
||||||
|
|
||||||
|
/// The fourth recommendation by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^14, block size of 8 and parallelism of 5.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp4 = ScryptSecurity('owasp4', N: 1 << 14, r: 8, p: 5);
|
||||||
|
|
||||||
|
/// The fifth recommendation by [OWASP][link].
|
||||||
|
///
|
||||||
|
/// It uses cost of 2^13, block size of 8 and parallelism of 10.
|
||||||
|
///
|
||||||
|
/// [link]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
static const owasp5 = ScryptSecurity('owasp5', N: 1 << 13, r: 8, p: 10);
|
||||||
|
}
|
||||||
163
hashlib/lib/src/algorithms/sha1.dart
Normal file
163
hashlib/lib/src/algorithms/sha1.dart
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x67452301, // a
|
||||||
|
0xEFCDAB89, // b
|
||||||
|
0x98BADCFE, // c
|
||||||
|
0x10325476, // d
|
||||||
|
0xC3D2E1F0, // e
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from The Internet Society
|
||||||
|
/// [US Secure Hash Algorithm 1 (SHA1)][rfc3174].
|
||||||
|
///
|
||||||
|
/// [rfc3174]: https://www.ietf.org/rfc/rfc3174.html
|
||||||
|
class SHA1Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
final Uint32List chunk;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
SHA1Hash()
|
||||||
|
: chunk = Uint32List(80),
|
||||||
|
hashLength = 160 >>> 3,
|
||||||
|
state = Uint32List.fromList(_iv),
|
||||||
|
super(512 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update(List<int> block, [int offset = 0, bool last = false]) {
|
||||||
|
var w = chunk;
|
||||||
|
|
||||||
|
int a, b, c, d, e;
|
||||||
|
int t, x, ch, i, j;
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
|
||||||
|
// Convert the block to chunk
|
||||||
|
i = 0;
|
||||||
|
j = offset;
|
||||||
|
for (; i < 16; i++, j += 4) {
|
||||||
|
w[i] = ((block[j] & 0xFF) << 24) |
|
||||||
|
((block[j + 1] & 0xFF) << 16) |
|
||||||
|
((block[j + 2] & 0xFF) << 8) |
|
||||||
|
(block[j + 3] & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the first 16 words into the remaining 64 words
|
||||||
|
for (i = 16; i < 80; i++) {
|
||||||
|
x = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
|
||||||
|
w[i] = (x << 1) | ((x & _mask32) >>> 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 20; i++) {
|
||||||
|
ch = (b & c) | ((~b) & d);
|
||||||
|
t = ((a << 5) & _mask32) | (a >>> 27);
|
||||||
|
x = t + ch + e + w[i] + 0x5A827999;
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = ((b << 30) & _mask32) | (b >>> 2);
|
||||||
|
b = a;
|
||||||
|
a = x & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < 40; i++) {
|
||||||
|
ch = (b ^ c ^ d);
|
||||||
|
t = ((a << 5) & _mask32) | (a >>> 27);
|
||||||
|
x = t + ch + e + w[i] + 0x6ED9EBA1;
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = ((b << 30) & _mask32) | (b >>> 2);
|
||||||
|
b = a;
|
||||||
|
a = x & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < 60; i++) {
|
||||||
|
ch = ((b & c) | (b & d) | (c & d));
|
||||||
|
t = ((a << 5) & _mask32) | (a >>> 27);
|
||||||
|
x = t + ch + e + w[i] + 0x8F1BBCDC;
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = ((b << 30) & _mask32) | (b >>> 2);
|
||||||
|
b = a;
|
||||||
|
a = x & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < 80; i++) {
|
||||||
|
ch = (b ^ c ^ d);
|
||||||
|
t = ((a << 5) & _mask32) | (a >>> 27);
|
||||||
|
x = t + ch + e + w[i] + 0xCA62C1D6;
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = ((b << 30) & _mask32) | (b >>> 2);
|
||||||
|
b = a;
|
||||||
|
a = x & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
int n = messageLengthInBits;
|
||||||
|
buffer[56] = n >>> 56;
|
||||||
|
buffer[57] = n >>> 48;
|
||||||
|
buffer[58] = n >>> 40;
|
||||||
|
buffer[59] = n >>> 32;
|
||||||
|
buffer[60] = n >>> 24;
|
||||||
|
buffer[61] = n >>> 16;
|
||||||
|
buffer[62] = n >>> 8;
|
||||||
|
buffer[63] = n;
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update(buffer);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
var bytes = Uint8List(hashLength);
|
||||||
|
for (int j = 0, i = 0; j < hashLength; i++, j += 4) {
|
||||||
|
bytes[j] = state[i] >>> 24;
|
||||||
|
bytes[j + 1] = state[i] >>> 16;
|
||||||
|
bytes[j + 2] = state[i] >>> 8;
|
||||||
|
bytes[j + 3] = state[i];
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
113
hashlib/lib/src/algorithms/sha2/sha2.dart
Normal file
113
hashlib/lib/src/algorithms/sha2/sha2.dart
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'sha2_1024_64bit.dart' if (dart.library.js) 'sha2_1024_32bit.dart';
|
||||||
|
import 'sha2_512.dart';
|
||||||
|
|
||||||
|
/// Implementation of 224-bit hash generator based on 512-bit SHA2.
|
||||||
|
class SHA224Hash extends SHA2of512 {
|
||||||
|
SHA224Hash()
|
||||||
|
: super(
|
||||||
|
hashLength: 224 >>> 3,
|
||||||
|
seed: [
|
||||||
|
0xC1059ED8, // a
|
||||||
|
0x367CD507, // b
|
||||||
|
0x3070DD17, // c
|
||||||
|
0xF70E5939, // d
|
||||||
|
0xFFC00B31, // e
|
||||||
|
0x68581511, // f
|
||||||
|
0x64F98FA7, // g
|
||||||
|
0xBEFA4FA4, // h
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 256-bit hash generator based on 512-bit SHA2.
|
||||||
|
class SHA256Hash extends SHA2of512 {
|
||||||
|
SHA256Hash()
|
||||||
|
: super(
|
||||||
|
hashLength: 256 >>> 3,
|
||||||
|
seed: [
|
||||||
|
0x6A09E667, // a
|
||||||
|
0xBB67AE85, // b
|
||||||
|
0x3C6EF372, // c
|
||||||
|
0xA54FF53A, // d
|
||||||
|
0x510E527F, // e
|
||||||
|
0x9B05688C, // f
|
||||||
|
0x1F83D9AB, // g
|
||||||
|
0x5BE0CD19, // h
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 384-bit hash generator based on 1024-bit SHA2.
|
||||||
|
class SHA384Hash extends SHA2of1024 {
|
||||||
|
SHA384Hash()
|
||||||
|
: super(
|
||||||
|
hashLength: 384 >>> 3,
|
||||||
|
seed: [
|
||||||
|
0xCBBB9D5D, 0xC1059ED8, // a
|
||||||
|
0x629A292A, 0x367CD507, // b
|
||||||
|
0x9159015A, 0x3070DD17, // c
|
||||||
|
0x152FECD8, 0xF70E5939, // d
|
||||||
|
0x67332667, 0xFFC00B31, // e
|
||||||
|
0x8EB44A87, 0x68581511, // f
|
||||||
|
0xDB0C2E0D, 0x64F98FA7, // g
|
||||||
|
0x47B5481D, 0xBEFA4FA4, // h
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 512-bit hash generator based on 1024-bit SHA2.
|
||||||
|
class SHA512Hash extends SHA2of1024 {
|
||||||
|
SHA512Hash()
|
||||||
|
: super(
|
||||||
|
hashLength: 512 >>> 3,
|
||||||
|
seed: [
|
||||||
|
0x6A09E667, 0xF3BCC908, // a
|
||||||
|
0xBB67AE85, 0x84CAA73B, // b
|
||||||
|
0x3C6EF372, 0xFE94F82B, // c
|
||||||
|
0xA54FF53A, 0x5F1D36F1, // d
|
||||||
|
0x510E527F, 0xADE682D1, // e
|
||||||
|
0x9B05688C, 0x2B3E6C1F, // f
|
||||||
|
0x1F83D9AB, 0xFB41BD6B, // g
|
||||||
|
0x5BE0CD19, 0x137E2179, // h
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 224-bit hash generator based on 1024-bit SHA2.
|
||||||
|
class SHA512t224Hash extends SHA2of1024 {
|
||||||
|
SHA512t224Hash()
|
||||||
|
: super(
|
||||||
|
hashLength: 224 >>> 3,
|
||||||
|
seed: [
|
||||||
|
0x8C3D37C8, 0x19544DA2, // a
|
||||||
|
0x73E19966, 0x89DCD4D6, // b
|
||||||
|
0x1DFAB7AE, 0x32FF9C82, // c
|
||||||
|
0x679DD514, 0x582F9FCF, // d
|
||||||
|
0x0F6D2B69, 0x7BD44DA8, // e
|
||||||
|
0x77E36F73, 0x04C48942, // f
|
||||||
|
0x3F9D85A8, 0x6A1D36C8, // g
|
||||||
|
0x1112E6AD, 0x91D692A1, // h
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 256-bit hash generator based on 1024-bit SHA2.
|
||||||
|
class SHA512t256Hash extends SHA2of1024 {
|
||||||
|
SHA512t256Hash()
|
||||||
|
: super(
|
||||||
|
hashLength: 256 >>> 3,
|
||||||
|
seed: [
|
||||||
|
0x22312194, 0xFC2BF72C, // a
|
||||||
|
0x9F555FA3, 0xC84C64C2, // b
|
||||||
|
0x2393B86B, 0x6F53B151, // c
|
||||||
|
0x96387719, 0x5940EABD, // d
|
||||||
|
0x96283EE2, 0xA88EFFE3, // e
|
||||||
|
0xBE5E1E25, 0x53863992, // f
|
||||||
|
0x2B0199FC, 0x2C85B8AA, // g
|
||||||
|
0x0EB72DDC, 0x81C52CA2, // h
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
305
hashlib/lib/src/algorithms/sha2/sha2_1024_32bit.dart
Normal file
305
hashlib/lib/src/algorithms/sha2/sha2_1024_32bit.dart
Normal file
|
|
@ -0,0 +1,305 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
// Initialize array of round 64-bit constants
|
||||||
|
const List<int> _k = [
|
||||||
|
0x428A2F98, 0xD728AE22, 0x71374491, 0x23EF65CD, 0xB5C0FBCF, 0xEC4D3B2F, //
|
||||||
|
0xE9B5DBA5, 0x8189DBBC, 0x3956C25B, 0xF348B538, 0x59F111F1, 0xB605D019,
|
||||||
|
0x923F82A4, 0xAF194F9B, 0xAB1C5ED5, 0xDA6D8118, 0xD807AA98, 0xA3030242,
|
||||||
|
0x12835B01, 0x45706FBE, 0x243185BE, 0x4EE4B28C, 0x550C7DC3, 0xD5FFB4E2,
|
||||||
|
0x72BE5D74, 0xF27B896F, 0x80DEB1FE, 0x3B1696B1, 0x9BDC06A7, 0x25C71235,
|
||||||
|
0xC19BF174, 0xCF692694, 0xE49B69C1, 0x9EF14AD2, 0xEFBE4786, 0x384F25E3,
|
||||||
|
0x0FC19DC6, 0x8B8CD5B5, 0x240CA1CC, 0x77AC9C65, 0x2DE92C6F, 0x592B0275,
|
||||||
|
0x4A7484AA, 0x6EA6E483, 0x5CB0A9DC, 0xBD41FBD4, 0x76F988DA, 0x831153B5,
|
||||||
|
0x983E5152, 0xEE66DFAB, 0xA831C66D, 0x2DB43210, 0xB00327C8, 0x98FB213F,
|
||||||
|
0xBF597FC7, 0xBEEF0EE4, 0xC6E00BF3, 0x3DA88FC2, 0xD5A79147, 0x930AA725,
|
||||||
|
0x06CA6351, 0xE003826F, 0x14292967, 0x0A0E6E70, 0x27B70A85, 0x46D22FFC,
|
||||||
|
0x2E1B2138, 0x5C26C926, 0x4D2C6DFC, 0x5AC42AED, 0x53380D13, 0x9D95B3DF,
|
||||||
|
0x650A7354, 0x8BAF63DE, 0x766A0ABB, 0x3C77B2A8, 0x81C2C92E, 0x47EDAEE6,
|
||||||
|
0x92722C85, 0x1482353B, 0xA2BFE8A1, 0x4CF10364, 0xA81A664B, 0xBC423001,
|
||||||
|
0xC24B8B70, 0xD0F89791, 0xC76C51A3, 0x0654BE30, 0xD192E819, 0xD6EF5218,
|
||||||
|
0xD6990624, 0x5565A910, 0xF40E3585, 0x5771202A, 0x106AA070, 0x32BBD1B8,
|
||||||
|
0x19A4C116, 0xB8D2D0C8, 0x1E376C08, 0x5141AB53, 0x2748774C, 0xDF8EEB99,
|
||||||
|
0x34B0BCB5, 0xE19B48A8, 0x391C0CB3, 0xC5C95A63, 0x4ED8AA4A, 0xE3418ACB,
|
||||||
|
0x5B9CCA4F, 0x7763E373, 0x682E6FF3, 0xD6B2B8A3, 0x748F82EE, 0x5DEFB2FC,
|
||||||
|
0x78A5636F, 0x43172F60, 0x84C87814, 0xA1F0AB72, 0x8CC70208, 0x1A6439EC,
|
||||||
|
0x90BEFFFA, 0x23631E28, 0xA4506CEB, 0xDE82BDE9, 0xBEF9A3F7, 0xB2C67915,
|
||||||
|
0xC67178F2, 0xE372532B, 0xCA273ECE, 0xEA26619C, 0xD186B8C7, 0x21C0C207,
|
||||||
|
0xEADA7DD6, 0xCDE0EB1E, 0xF57D4F7F, 0xEE6ED178, 0x06F067AA, 0x72176FBA,
|
||||||
|
0x0A637DC5, 0xA2C898A6, 0x113F9804, 0xBEF90DAE, 0x1B710B35, 0x131C471B,
|
||||||
|
0x28DB77F5, 0x23047D84, 0x32CAAB7B, 0x40C72493, 0x3C9EBE0A, 0x15C9BEBC,
|
||||||
|
0x431D67C4, 0x9C100D4C, 0x4CC5D4BE, 0xCB3E42B6, 0x597F299C, 0xFC657E2A,
|
||||||
|
0x5FCB6FAB, 0x3AD6FAEC, 0x6C44198C, 0x4A475817,
|
||||||
|
];
|
||||||
|
|
||||||
|
const int _sig1 = 0;
|
||||||
|
const int _sig2 = _sig1 + 2;
|
||||||
|
const int _sig3 = _sig2 + 2;
|
||||||
|
const int _sig4 = _sig3 + 2;
|
||||||
|
const int _a = _sig4 + 2;
|
||||||
|
const int _b = _a + 2;
|
||||||
|
const int _c = _b + 2;
|
||||||
|
const int _d = _c + 2;
|
||||||
|
const int _e = _d + 2;
|
||||||
|
const int _f = _e + 2;
|
||||||
|
const int _g = _f + 2;
|
||||||
|
const int _h = _g + 2;
|
||||||
|
const int _t1 = _h + 2;
|
||||||
|
const int _t2 = _t1 + 2;
|
||||||
|
const int _t3 = _t2 + 2;
|
||||||
|
const int _t4 = _t3 + 2;
|
||||||
|
const int _t5 = _t4 + 2;
|
||||||
|
|
||||||
|
/// Implementation is derived from [RFC6234][rfc6234] which follows the
|
||||||
|
/// [FIPS 180-4][fips180] standard for SHA and SHA-based HMAC and HKDF.
|
||||||
|
///
|
||||||
|
/// It uses 32-bit integers to accommodate 64-bit integer operations, designed
|
||||||
|
/// specially to be supported by Web VM. It is albeit slower than the native
|
||||||
|
/// implementation.
|
||||||
|
///
|
||||||
|
/// [rfc6234]: https://www.ietf.org/rfc/rfc6234.html
|
||||||
|
/// [fips180]: https://csrc.nist.gov/publications/detail/fips/180/4/final
|
||||||
|
class SHA2of1024 extends BlockHashSink {
|
||||||
|
final List<int> seed;
|
||||||
|
final Uint32List state;
|
||||||
|
final Uint32List chunk;
|
||||||
|
final _var = Uint32List(_t5 + 2);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
/// For internal use only.
|
||||||
|
SHA2of1024({
|
||||||
|
required this.seed,
|
||||||
|
required this.hashLength,
|
||||||
|
}) : chunk = Uint32List(160),
|
||||||
|
state = Uint32List.fromList(seed),
|
||||||
|
super(1024 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, seed);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// z = x ^ y
|
||||||
|
static void _xor(List<int> x, int i, List<int> y, int j, List<int> z, int k) {
|
||||||
|
z[k] = x[i] ^ y[j];
|
||||||
|
z[k + 1] = x[i + 1] ^ y[j + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// z = x + y
|
||||||
|
static void _add(List<int> x, int i, List<int> y, int j, List<int> z, int k) {
|
||||||
|
z[k + 1] = x[i + 1] + y[j + 1];
|
||||||
|
z[k] = x[i] + y[j] + (z[k + 1] < x[i + 1] ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x += z
|
||||||
|
static void _addAndSet(List<int> x, int i, List<int> z, int j) {
|
||||||
|
var t = x[i + 1];
|
||||||
|
x[i + 1] += z[j + 1];
|
||||||
|
x[i] += z[j] + (x[i + 1] < t ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// x >>> n
|
||||||
|
static void _shr(int n, List<int> x, int i, List<int> z, int k) {
|
||||||
|
var a = x[i];
|
||||||
|
var b = x[i + 1];
|
||||||
|
if (n == 32) {
|
||||||
|
z[k] = 0;
|
||||||
|
z[k + 1] = a;
|
||||||
|
} else if (n < 32) {
|
||||||
|
z[k] = a >>> n;
|
||||||
|
z[k + 1] = (a << (32 - n)) | (b >>> n);
|
||||||
|
} else {
|
||||||
|
z[k] = 0;
|
||||||
|
z[k + 1] = a >>> (n - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (x << (64 - n)) | (x >>> n)
|
||||||
|
static void _rotr(int n, List<int> x, int i, List<int> z, int k) {
|
||||||
|
var a = x[i];
|
||||||
|
var b = x[i + 1];
|
||||||
|
if (n == 32) {
|
||||||
|
z[k] = b;
|
||||||
|
z[k + 1] = a;
|
||||||
|
} else if (n < 32) {
|
||||||
|
z[k] = (b << (32 - n)) | (a >>> n);
|
||||||
|
z[k + 1] = (a << (32 - n)) | (b >>> n);
|
||||||
|
} else {
|
||||||
|
z[k] = (a << (64 - n)) | (b >>> (n - 32));
|
||||||
|
z[k + 1] = (b << (64 - n)) | (a >>> (n - 32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = _rotr(x, 28) ^ _rotr(x, 34) ^ _rotr(x, 39)
|
||||||
|
void _bsig0(List<int> x, int i, List<int> z, int j) {
|
||||||
|
_rotr(28, x, i, _var, _sig1);
|
||||||
|
_rotr(34, x, i, _var, _sig2);
|
||||||
|
_rotr(39, x, i, _var, _sig3);
|
||||||
|
_xor(_var, _sig2, _var, _sig3, _var, _sig4);
|
||||||
|
_xor(_var, _sig1, _var, _sig4, z, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = _rotr(x, 14) ^ _rotr(x, 18) ^ _rotr(x, 41)
|
||||||
|
void _bsig1(List<int> x, int i, List<int> z, int j) {
|
||||||
|
_rotr(14, x, i, _var, _sig1);
|
||||||
|
_rotr(18, x, i, _var, _sig2);
|
||||||
|
_rotr(41, x, i, _var, _sig3);
|
||||||
|
_xor(_var, _sig2, _var, _sig3, _var, _sig4);
|
||||||
|
_xor(_var, _sig1, _var, _sig4, z, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = _rotr(x, 1) ^ _rotr(x, 8) ^ (x >>> 7)
|
||||||
|
void _ssig0(List<int> x, int i, List<int> z, int j) {
|
||||||
|
_rotr(1, x, i, _var, _sig1);
|
||||||
|
_rotr(8, x, i, _var, _sig2);
|
||||||
|
_shr(7, x, i, _var, _sig3);
|
||||||
|
_xor(_var, _sig2, _var, _sig3, _var, _sig4);
|
||||||
|
_xor(_var, _sig1, _var, _sig4, z, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = _rotr(x, 19) ^ _rotr(x, 61) ^ (x >>> 6)
|
||||||
|
void _ssig1(List<int> x, int i, List<int> z, int j) {
|
||||||
|
_rotr(19, x, i, _var, _sig1);
|
||||||
|
_rotr(61, x, i, _var, _sig2);
|
||||||
|
_shr(6, x, i, _var, _sig3);
|
||||||
|
_xor(_var, _sig2, _var, _sig3, _var, _sig4);
|
||||||
|
_xor(_var, _sig1, _var, _sig4, z, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = (e & f) ^ ((~e) & g)
|
||||||
|
static void _ch(List<int> e, int i, List<int> f, int j, List<int> g, int k,
|
||||||
|
List<int> z, int l) {
|
||||||
|
z[l] = (e[i] & (f[j] ^ g[k])) ^ g[k];
|
||||||
|
z[l + 1] = (e[i + 1] & (f[j + 1] ^ g[k + 1])) ^ g[k + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// z = (a & b) ^ (a & c) ^ (b & c)
|
||||||
|
static void _maj(List<int> a, int i, List<int> b, int j, List<int> c, int k,
|
||||||
|
List<int> z, int l) {
|
||||||
|
z[l] = (a[i] & (b[j] | c[k])) | (b[j] & c[k]);
|
||||||
|
z[l + 1] = (a[i + 1] & (b[j + 1] | c[k + 1])) | (b[j + 1] & c[k + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update(List<int> block, [int offset = 0, bool last = false]) {
|
||||||
|
var w = chunk;
|
||||||
|
|
||||||
|
_var.setAll(_a, state);
|
||||||
|
|
||||||
|
// Convert the block to chunk
|
||||||
|
for (int i = 0, j = offset; i < 32; i++, j += 4) {
|
||||||
|
w[i] = ((block[j] & 0xFF) << 24) |
|
||||||
|
((block[j + 1] & 0xFF) << 16) |
|
||||||
|
((block[j + 2] & 0xFF) << 8) |
|
||||||
|
((block[j + 3] & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the first 32 words into nest 160 words
|
||||||
|
for (var i = 32; i < 160; i += 2) {
|
||||||
|
// w[i] = _ssig1(w[i - 2]) + w[i - 7] + _ssig0(w[i - 15]) + w[i - 16];
|
||||||
|
_ssig1(w, i - 4, _var, _t1);
|
||||||
|
_add(_var, _t1, w, i - 14, _var, _t2);
|
||||||
|
_ssig0(w, i - 30, _var, _t1);
|
||||||
|
_add(_var, _t1, w, i - 32, _var, _t3);
|
||||||
|
_add(_var, _t2, _var, _t3, w, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 160; i += 2) {
|
||||||
|
// t1 = h + _bsig1(e) + _ch(e, f, g) + k[i] + w[i];
|
||||||
|
_bsig1(_var, _e, _var, _t1);
|
||||||
|
_add(_var, _h, _var, _t1, _var, _t2);
|
||||||
|
_ch(_var, _e, _var, _f, _var, _g, _var, _t3);
|
||||||
|
_add(_var, _t2, _var, _t3, _var, _t4);
|
||||||
|
_add(_k, i, w, i, _var, _t5);
|
||||||
|
_add(_var, _t4, _var, _t5, _var, _t1);
|
||||||
|
|
||||||
|
// t2 = _bsig0(A) + _maj(a, b, c);
|
||||||
|
_bsig0(_var, _a, _var, _t3);
|
||||||
|
_maj(_var, _a, _var, _b, _var, _c, _var, _t4);
|
||||||
|
_add(_var, _t3, _var, _t4, _var, _t2);
|
||||||
|
|
||||||
|
// h = g;
|
||||||
|
_var[_h] = _var[_g];
|
||||||
|
_var[_h + 1] = _var[_g + 1];
|
||||||
|
// g = f;
|
||||||
|
_var[_g] = _var[_f];
|
||||||
|
_var[_g + 1] = _var[_f + 1];
|
||||||
|
// f = e;
|
||||||
|
_var[_f] = _var[_e];
|
||||||
|
_var[_f + 1] = _var[_e + 1];
|
||||||
|
// e = d + t1;
|
||||||
|
_add(_var, _d, _var, _t1, _var, _e);
|
||||||
|
// d = c;
|
||||||
|
_var[_d] = _var[_c];
|
||||||
|
_var[_d + 1] = _var[_c + 1];
|
||||||
|
// c = b;
|
||||||
|
_var[_c] = _var[_b];
|
||||||
|
_var[_c + 1] = _var[_b + 1];
|
||||||
|
// b = a;
|
||||||
|
_var[_b] = _var[_a];
|
||||||
|
_var[_b + 1] = _var[_a + 1];
|
||||||
|
// a = t1 + t2;
|
||||||
|
_add(_var, _t1, _var, _t2, _var, _a);
|
||||||
|
}
|
||||||
|
|
||||||
|
_addAndSet(state, 0, _var, _a);
|
||||||
|
_addAndSet(state, 2, _var, _b);
|
||||||
|
_addAndSet(state, 4, _var, _c);
|
||||||
|
_addAndSet(state, 6, _var, _d);
|
||||||
|
_addAndSet(state, 8, _var, _e);
|
||||||
|
_addAndSet(state, 10, _var, _f);
|
||||||
|
_addAndSet(state, 12, _var, _g);
|
||||||
|
_addAndSet(state, 14, _var, _h);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 112) {
|
||||||
|
for (; pos < 128; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 120; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
int n = messageLengthInBits;
|
||||||
|
buffer[120] = n >>> 56;
|
||||||
|
buffer[121] = n >>> 48;
|
||||||
|
buffer[122] = n >>> 40;
|
||||||
|
buffer[123] = n >>> 32;
|
||||||
|
buffer[124] = n >>> 24;
|
||||||
|
buffer[125] = n >>> 16;
|
||||||
|
buffer[126] = n >>> 8;
|
||||||
|
buffer[127] = n;
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update(buffer);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
var bytes = Uint8List(hashLength);
|
||||||
|
for (int j = 0, i = 0; j < hashLength; i++, j += 4) {
|
||||||
|
bytes[j] = state[i] >>> 24;
|
||||||
|
bytes[j + 1] = state[i] >>> 16;
|
||||||
|
bytes[j + 2] = state[i] >>> 8;
|
||||||
|
bytes[j + 3] = state[i];
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
208
hashlib/lib/src/algorithms/sha2/sha2_1024_64bit.dart
Normal file
208
hashlib/lib/src/algorithms/sha2/sha2_1024_64bit.dart
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
// Initialize array of round 64-bit constants
|
||||||
|
const List<int> _k = [
|
||||||
|
0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, //
|
||||||
|
0xE9B5DBA58189DBBC, 0x3956C25BF348B538, 0x59F111F1B605D019,
|
||||||
|
0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, 0xD807AA98A3030242,
|
||||||
|
0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2,
|
||||||
|
0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235,
|
||||||
|
0xC19BF174CF692694, 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3,
|
||||||
|
0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, 0x2DE92C6F592B0275,
|
||||||
|
0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5,
|
||||||
|
0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F,
|
||||||
|
0xBF597FC7BEEF0EE4, 0xC6E00BF33DA88FC2, 0xD5A79147930AA725,
|
||||||
|
0x06CA6351E003826F, 0x142929670A0E6E70, 0x27B70A8546D22FFC,
|
||||||
|
0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF,
|
||||||
|
0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6,
|
||||||
|
0x92722C851482353B, 0xA2BFE8A14CF10364, 0xA81A664BBC423001,
|
||||||
|
0xC24B8B70D0F89791, 0xC76C51A30654BE30, 0xD192E819D6EF5218,
|
||||||
|
0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8,
|
||||||
|
0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99,
|
||||||
|
0x34B0BCB5E19B48A8, 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB,
|
||||||
|
0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, 0x748F82EE5DEFB2FC,
|
||||||
|
0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC,
|
||||||
|
0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915,
|
||||||
|
0xC67178F2E372532B, 0xCA273ECEEA26619C, 0xD186B8C721C0C207,
|
||||||
|
0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, 0x06F067AA72176FBA,
|
||||||
|
0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B,
|
||||||
|
0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC,
|
||||||
|
0x431D67C49C100D4C, 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A,
|
||||||
|
0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Implementation is derived from [RFC6234][rfc6234] which follows the
|
||||||
|
/// [FIPS 180-4][fips180] standard for SHA and SHA-based HMAC and HKDF.
|
||||||
|
///
|
||||||
|
/// It uses 64-bit integer operations internally which is not supported by
|
||||||
|
/// Web VM, but a lot faster.
|
||||||
|
///
|
||||||
|
/// [rfc6234]: https://www.ietf.org/rfc/rfc6234.html
|
||||||
|
/// [fips180]: https://csrc.nist.gov/publications/detail/fips/180/4/final
|
||||||
|
class SHA2of1024 extends BlockHashSink {
|
||||||
|
final List<int> seed;
|
||||||
|
final Uint32List state;
|
||||||
|
final Uint64List chunk;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
/// For internal use only.
|
||||||
|
SHA2of1024({
|
||||||
|
required this.seed,
|
||||||
|
required this.hashLength,
|
||||||
|
}) : chunk = Uint64List(80),
|
||||||
|
state = Uint32List.fromList(seed),
|
||||||
|
super(1024 >>> 3);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, seed);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates 64-bit number x by n bits
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _bsig0(int x) =>
|
||||||
|
((x >>> 28) | (x << 36)) ^
|
||||||
|
((x >>> 34) | (x << 30)) ^
|
||||||
|
((x >>> 39) | (x << 25));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _bsig1(int x) =>
|
||||||
|
((x >>> 14) | (x << 50)) ^
|
||||||
|
((x >>> 18) | (x << 46)) ^
|
||||||
|
((x >>> 41) | (x << 23));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _ssig0(int x) =>
|
||||||
|
((x >>> 1) | (x << 63)) ^ ((x >>> 8) | (x << 56)) ^ (x >>> 7);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _ssig1(int x) =>
|
||||||
|
((x >>> 19) | (x << 45)) ^ ((x >>> 61) | (x << 3)) ^ (x >>> 6);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update(List<int> block, [int offset = 0, bool last = false]) {
|
||||||
|
// Convert the block to chunk
|
||||||
|
for (int i = 0, j = offset; i < 16; i++, j += 8) {
|
||||||
|
chunk[i] = ((block[j] & 0xFF) << 56) |
|
||||||
|
((block[j + 1] & 0xFF) << 48) |
|
||||||
|
((block[j + 2] & 0xFF) << 40) |
|
||||||
|
((block[j + 3] & 0xFF) << 32) |
|
||||||
|
((block[j + 4] & 0xFF) << 24) |
|
||||||
|
((block[j + 5] & 0xFF) << 16) |
|
||||||
|
((block[j + 6] & 0xFF) << 8) |
|
||||||
|
(block[j + 7] & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
var w = chunk;
|
||||||
|
int t1, t2, ch, maj;
|
||||||
|
int a, b, c, d, e, f, g, h;
|
||||||
|
int ta, tb, tc, td, te, tf, tg, th;
|
||||||
|
|
||||||
|
ta = a = (state[0] << 32) | (state[1]);
|
||||||
|
tb = b = (state[2] << 32) | (state[3]);
|
||||||
|
tc = c = (state[4] << 32) | (state[5]);
|
||||||
|
td = d = (state[6] << 32) | (state[7]);
|
||||||
|
te = e = (state[8] << 32) | (state[9]);
|
||||||
|
tf = f = (state[10] << 32) | (state[11]);
|
||||||
|
tg = g = (state[12] << 32) | (state[13]);
|
||||||
|
th = h = (state[14] << 32) | (state[15]);
|
||||||
|
|
||||||
|
// Extend the first 16 words into the 80 words (64-bit)
|
||||||
|
for (int i = 16; i < 80; i++) {
|
||||||
|
w[i] = _ssig1(w[i - 2]) + w[i - 7] + _ssig0(w[i - 15]) + w[i - 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 80; ++i) {
|
||||||
|
ch = (e & f) ^ ((~e) & g);
|
||||||
|
maj = (a & b) ^ (a & c) ^ (b & c);
|
||||||
|
t1 = h + _bsig1(e) + ch + _k[i] + w[i];
|
||||||
|
t2 = _bsig0(a) + maj;
|
||||||
|
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = d + t1;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = t1 + t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ta += a;
|
||||||
|
tb += b;
|
||||||
|
tc += c;
|
||||||
|
td += d;
|
||||||
|
te += e;
|
||||||
|
tf += f;
|
||||||
|
tg += g;
|
||||||
|
th += h;
|
||||||
|
state[0] = ta >>> 32;
|
||||||
|
state[1] = ta;
|
||||||
|
state[2] = tb >>> 32;
|
||||||
|
state[3] = tb;
|
||||||
|
state[4] = tc >>> 32;
|
||||||
|
state[5] = tc;
|
||||||
|
state[6] = td >>> 32;
|
||||||
|
state[7] = td;
|
||||||
|
state[8] = te >>> 32;
|
||||||
|
state[9] = te;
|
||||||
|
state[10] = tf >>> 32;
|
||||||
|
state[11] = tf;
|
||||||
|
state[12] = tg >>> 32;
|
||||||
|
state[13] = tg;
|
||||||
|
state[14] = th >>> 32;
|
||||||
|
state[15] = th;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 112) {
|
||||||
|
for (; pos < 128; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 120; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
int n = messageLengthInBits;
|
||||||
|
buffer[120] = n >>> 56;
|
||||||
|
buffer[121] = n >>> 48;
|
||||||
|
buffer[122] = n >>> 40;
|
||||||
|
buffer[123] = n >>> 32;
|
||||||
|
buffer[124] = n >>> 24;
|
||||||
|
buffer[125] = n >>> 16;
|
||||||
|
buffer[126] = n >>> 8;
|
||||||
|
buffer[127] = n;
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update(buffer);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
var bytes = Uint8List(hashLength);
|
||||||
|
for (int j = 0, i = 0; j < hashLength; i++, j += 4) {
|
||||||
|
bytes[j] = state[i] >>> 24;
|
||||||
|
bytes[j + 1] = state[i] >>> 16;
|
||||||
|
bytes[j + 2] = state[i] >>> 8;
|
||||||
|
bytes[j + 3] = state[i];
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
179
hashlib/lib/src/algorithms/sha2/sha2_512.dart
Normal file
179
hashlib/lib/src/algorithms/sha2/sha2_512.dart
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
// Initialize array of round constants
|
||||||
|
const List<int> _k = [
|
||||||
|
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, //
|
||||||
|
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
||||||
|
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
||||||
|
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
||||||
|
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
||||||
|
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
||||||
|
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
||||||
|
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
||||||
|
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
||||||
|
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
||||||
|
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
||||||
|
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
||||||
|
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
||||||
|
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
||||||
|
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
||||||
|
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Implementation is derived from [RFC6234][rfc6234] which follows the
|
||||||
|
/// [FIPS 180-4][fips180] standard for SHA and SHA-based HMAC and HKDF.
|
||||||
|
///
|
||||||
|
/// [rfc6234]: https://www.ietf.org/rfc/rfc6234.html
|
||||||
|
/// [fips180]: https://csrc.nist.gov/publications/detail/fips/180/4/final
|
||||||
|
class SHA2of512 extends BlockHashSink {
|
||||||
|
final List<int> seed;
|
||||||
|
final Uint32List state;
|
||||||
|
final Uint32List chunk;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
/// For internal use only.
|
||||||
|
SHA2of512({
|
||||||
|
required this.seed,
|
||||||
|
required this.hashLength,
|
||||||
|
}) : chunk = Uint32List(64),
|
||||||
|
state = Uint32List.fromList(seed),
|
||||||
|
super(64);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, seed);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates x right by n bits.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _bsig0(int x) =>
|
||||||
|
(((x & _mask32) >>> 2) | ((x << 30) & _mask32)) ^
|
||||||
|
(((x & _mask32) >>> 13) | ((x << 19) & _mask32)) ^
|
||||||
|
(((x & _mask32) >>> 22) | ((x << 10) & _mask32));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _bsig1(int x) =>
|
||||||
|
(((x & _mask32) >>> 6) | ((x << 26) & _mask32)) ^
|
||||||
|
(((x & _mask32) >>> 11) | ((x << 21) & _mask32)) ^
|
||||||
|
(((x & _mask32) >>> 25) | ((x << 7) & _mask32));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _ssig0(int x) =>
|
||||||
|
(((x & _mask32) >>> 7) | ((x << 25) & _mask32)) ^
|
||||||
|
(((x & _mask32) >>> 18) | ((x << 14) & _mask32)) ^
|
||||||
|
(x >>> 3);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _ssig1(int x) =>
|
||||||
|
(((x & _mask32) >>> 17) | ((x << 15) & _mask32)) ^
|
||||||
|
(((x & _mask32) >>> 19) | ((x << 13) & _mask32)) ^
|
||||||
|
(x >>> 10);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update(List<int> block, [int offset = 0, bool last = false]) {
|
||||||
|
// Convert the block to chunk
|
||||||
|
for (int i = 0, j = offset; i < 16; i++, j += 4) {
|
||||||
|
chunk[i] = ((block[j] & 0xFF) << 24) |
|
||||||
|
((block[j + 1] & 0xFF) << 16) |
|
||||||
|
((block[j + 2] & 0xFF) << 8) |
|
||||||
|
(block[j + 3] & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
var w = chunk;
|
||||||
|
int t1, t2, ch, maj;
|
||||||
|
int a, b, c, d, e, f, g, h;
|
||||||
|
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
f = state[5];
|
||||||
|
g = state[6];
|
||||||
|
h = state[7];
|
||||||
|
|
||||||
|
// Extend the first 16 words into the 64 words
|
||||||
|
for (int i = 16; i < 64; i++) {
|
||||||
|
w[i] = _ssig1(w[i - 2]) + w[i - 7] + _ssig0(w[i - 15]) + w[i - 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 64; ++i) {
|
||||||
|
ch = (e & f) ^ ((~e) & g);
|
||||||
|
maj = (a & b) ^ (a & c) ^ (b & c);
|
||||||
|
t1 = h + _bsig1(e) + ch + _k[i] + w[i];
|
||||||
|
t2 = _bsig0(a) + maj;
|
||||||
|
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = (d + t1) & _mask32;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = (t1 + t2) & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += e;
|
||||||
|
state[5] += f;
|
||||||
|
state[6] += g;
|
||||||
|
state[7] += h;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
int n = messageLengthInBits;
|
||||||
|
buffer[56] = n >>> 56;
|
||||||
|
buffer[57] = n >>> 48;
|
||||||
|
buffer[58] = n >>> 40;
|
||||||
|
buffer[59] = n >>> 32;
|
||||||
|
buffer[60] = n >>> 24;
|
||||||
|
buffer[61] = n >>> 16;
|
||||||
|
buffer[62] = n >>> 8;
|
||||||
|
buffer[63] = n;
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update(buffer);
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
var bytes = Uint8List(hashLength);
|
||||||
|
for (int j = 0, i = 0; j < hashLength; i++, j += 4) {
|
||||||
|
bytes[j] = state[i] >>> 24;
|
||||||
|
bytes[j + 1] = state[i] >>> 16;
|
||||||
|
bytes[j + 2] = state[i] >>> 8;
|
||||||
|
bytes[j + 3] = state[i];
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
hashlib/lib/src/algorithms/sha3.dart
Normal file
44
hashlib/lib/src/algorithms/sha3.dart
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'keccak/keccak.dart';
|
||||||
|
|
||||||
|
/// Implementation of 224-bit hash generator based on [KeccakHash]
|
||||||
|
/// with SHA3 padding.
|
||||||
|
class SHA3d224Hash extends KeccakHash {
|
||||||
|
SHA3d224Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 224 >>> 3,
|
||||||
|
paddingByte: 0x06,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 256-bit hash generator based on [KeccakHash]
|
||||||
|
/// with SHA3 padding.
|
||||||
|
class SHA3d256Hash extends KeccakHash {
|
||||||
|
SHA3d256Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 256 >>> 3,
|
||||||
|
paddingByte: 0x06,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 384-bit hash generator based on [KeccakHash]
|
||||||
|
/// with SHA3 padding.
|
||||||
|
class SHA3d384Hash extends KeccakHash {
|
||||||
|
SHA3d384Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 384 >>> 3,
|
||||||
|
paddingByte: 0x06,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 512-bit hash generator based on [KeccakHash]
|
||||||
|
/// with SHA3 padding.
|
||||||
|
class SHA3d512Hash extends KeccakHash {
|
||||||
|
SHA3d512Hash()
|
||||||
|
: super(
|
||||||
|
stateSize: 512 >>> 3,
|
||||||
|
paddingByte: 0x06,
|
||||||
|
);
|
||||||
|
}
|
||||||
36
hashlib/lib/src/algorithms/shake.dart
Normal file
36
hashlib/lib/src/algorithms/shake.dart
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'keccak/keccak.dart';
|
||||||
|
|
||||||
|
/// Implementation of 128-bit arbitrary hash generator based on [KeccakHash].
|
||||||
|
class Shake128Hash extends KeccakHash {
|
||||||
|
/// Create a SHAKE-128 hash generator with arbitrary output size.
|
||||||
|
///
|
||||||
|
/// If [outputLengthInBytes] is 0, it will generate an infinite sequence of
|
||||||
|
/// numbers with generate(), but the digest() will return an empty string.
|
||||||
|
///
|
||||||
|
/// If [outputLengthInBytes] is null, 128-bit output is generated by default.
|
||||||
|
Shake128Hash([int? outputLengthInBytes])
|
||||||
|
: super(
|
||||||
|
stateSize: 128 >>> 3,
|
||||||
|
paddingByte: 0x1f,
|
||||||
|
outputSize: outputLengthInBytes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of 256-bit arbitrary hash generator based on [KeccakHash].
|
||||||
|
class Shake256Hash extends KeccakHash {
|
||||||
|
/// Create a SHAKE-256 hash generator with arbitrary output size.
|
||||||
|
///
|
||||||
|
/// If [outputLengthInBytes] is 0, it will generate an infinite sequence of
|
||||||
|
/// numbers with generate(), but the digest() will return an empty string.
|
||||||
|
///
|
||||||
|
/// If [outputLengthInBytes] is null, 256-bit output is generated by default.
|
||||||
|
Shake256Hash([int? outputLengthInBytes])
|
||||||
|
: super(
|
||||||
|
stateSize: 256 >>> 3,
|
||||||
|
paddingByte: 0x1f,
|
||||||
|
outputSize: outputLengthInBytes,
|
||||||
|
);
|
||||||
|
}
|
||||||
176
hashlib/lib/src/algorithms/sm3.dart
Normal file
176
hashlib/lib/src/algorithms/sm3.dart
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const _iv = <int>[
|
||||||
|
0x7380166f,
|
||||||
|
0x4914b2b9,
|
||||||
|
0x172442d7,
|
||||||
|
0xda8a0600,
|
||||||
|
0xa96f30bc,
|
||||||
|
0x163138aa,
|
||||||
|
0xe38dee4d,
|
||||||
|
0xb0fb0e4e,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from the draft of
|
||||||
|
/// [The SM3 Cryptographic Hash Function][rfc].
|
||||||
|
///
|
||||||
|
/// [rfc]: https://datatracker.ietf.org/doc/draft-sca-cfrg-sm3/
|
||||||
|
class SM3Hash extends BlockHashSink {
|
||||||
|
final Uint32List state;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength;
|
||||||
|
|
||||||
|
SM3Hash()
|
||||||
|
: state = Uint32List.fromList(_iv),
|
||||||
|
hashLength = 256 >>> 3,
|
||||||
|
super(
|
||||||
|
512 >>> 3,
|
||||||
|
bufferLength: 68 << 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state.setAll(0, _iv);
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _swap32(int x) =>
|
||||||
|
((x << 24) & 0xff000000) |
|
||||||
|
((x << 8) & 0x00ff0000) |
|
||||||
|
((x >>> 8) & 0x0000ff00) |
|
||||||
|
((x >>> 24) & 0x000000ff);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int a, b, c, d, e, f, g, h;
|
||||||
|
int i, t, ss1, ss2, tt1, tt2;
|
||||||
|
var x = sbuffer;
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
f = state[5];
|
||||||
|
g = state[6];
|
||||||
|
h = state[7];
|
||||||
|
|
||||||
|
// Message Expansion
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
x[i] = _swap32(x[i]);
|
||||||
|
}
|
||||||
|
for (i = 16; i < 68; i++) {
|
||||||
|
t = x[i - 16] ^ x[i - 9] ^ _rotl32(x[i - 3], 15);
|
||||||
|
t ^= _rotl32(t, 15) ^ _rotl32(t, 23);
|
||||||
|
t ^= _rotl32(x[i - 13], 7) ^ x[i - 6];
|
||||||
|
x[i] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compression Function
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
t = _rotl32(a, 12) + e + _rotl32(0x79cc4519, i);
|
||||||
|
ss1 = _rotl32(t, 7);
|
||||||
|
ss2 = ss1 ^ _rotl32(a, 12);
|
||||||
|
t = a ^ b ^ c;
|
||||||
|
tt1 = (t + d + ss2 + (x[i] ^ x[i + 4])) & _mask32;
|
||||||
|
t = e ^ f ^ g;
|
||||||
|
tt2 = (t + h + ss1 + x[i]) & _mask32;
|
||||||
|
d = c;
|
||||||
|
c = _rotl32(b, 9);
|
||||||
|
b = a;
|
||||||
|
a = tt1;
|
||||||
|
h = g;
|
||||||
|
g = _rotl32(f, 19);
|
||||||
|
f = e;
|
||||||
|
e = tt2 ^ _rotl32(tt2, 9) ^ _rotl32(tt2, 17);
|
||||||
|
}
|
||||||
|
for (i = 16; i < 64; i++) {
|
||||||
|
t = _rotl32(a, 12) + e + _rotl32(0x7a879d8a, i & 31);
|
||||||
|
ss1 = _rotl32(t, 7);
|
||||||
|
ss2 = ss1 ^ _rotl32(a, 12);
|
||||||
|
t = (a & b) | (b & c) | (c & a);
|
||||||
|
tt1 = (t + d + ss2 + (x[i] ^ x[i + 4])) & _mask32;
|
||||||
|
t = (e & f) | (g & (~e));
|
||||||
|
tt2 = (t + h + ss1 + x[i]) & _mask32;
|
||||||
|
d = c;
|
||||||
|
c = _rotl32(b, 9);
|
||||||
|
b = a;
|
||||||
|
a = tt1;
|
||||||
|
h = g;
|
||||||
|
g = _rotl32(f, 19);
|
||||||
|
f = e;
|
||||||
|
e = tt2 ^ _rotl32(tt2, 9) ^ _rotl32(tt2, 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
state[0] ^= a;
|
||||||
|
state[1] ^= b;
|
||||||
|
state[2] ^= c;
|
||||||
|
state[3] ^= d;
|
||||||
|
state[4] ^= e;
|
||||||
|
state[5] ^= f;
|
||||||
|
state[6] ^= g;
|
||||||
|
state[7] ^= h;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
// Adding the signature byte
|
||||||
|
buffer[pos++] = 0x80;
|
||||||
|
|
||||||
|
// If no more space left in buffer for the message length
|
||||||
|
if (pos > 56) {
|
||||||
|
for (; pos < 64; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining buffer to put the message length at the end
|
||||||
|
for (; pos < 56; pos++) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append original message length in bits to message
|
||||||
|
bdata.setUint32(56, messageLengthInBits >>> 32, Endian.big);
|
||||||
|
bdata.setUint32(60, messageLengthInBits, Endian.big);
|
||||||
|
|
||||||
|
// Update with the final block
|
||||||
|
$update();
|
||||||
|
|
||||||
|
// Convert the state to 8-bit byte array
|
||||||
|
var output = Uint32List(hashLength >>> 2);
|
||||||
|
for (int i = 0; i < output.length; i++) {
|
||||||
|
output[i] = _swap32(state[i]);
|
||||||
|
}
|
||||||
|
return Uint8List.view(output.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
hashlib/lib/src/algorithms/xxh3/xxh3_128.dart
Normal file
4
hashlib/lib/src/algorithms/xxh3/xxh3_128.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
export 'xxh3_128_64bit.dart' if (dart.library.js) 'xxh3_128_32bit.dart';
|
||||||
32
hashlib/lib/src/algorithms/xxh3/xxh3_128_32bit.dart
Normal file
32
hashlib/lib/src/algorithms/xxh3/xxh3_128_32bit.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
/// Stub for 32-bit machines.
|
||||||
|
class XXH3Sink128bit extends BlockHashSink {
|
||||||
|
@override
|
||||||
|
final int hashLength = 16;
|
||||||
|
|
||||||
|
factory XXH3Sink128bit.withSeed(int seed) {
|
||||||
|
seed;
|
||||||
|
throw UnimplementedError('XXH3-128 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
factory XXH3Sink128bit.withSecret([List<int>? secret]) {
|
||||||
|
secret;
|
||||||
|
throw UnimplementedError('XXH3-128 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
throw UnimplementedError('XXH3-128 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
throw UnimplementedError('XXH3-128 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
}
|
||||||
509
hashlib/lib/src/algorithms/xxh3/xxh3_128_64bit.dart
Normal file
509
hashlib/lib/src/algorithms/xxh3/xxh3_128_64bit.dart
Normal file
|
|
@ -0,0 +1,509 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:collection';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const int _stripeLen = 64;
|
||||||
|
const int _midsizeMax = 240;
|
||||||
|
const int _minSecretSize = 136;
|
||||||
|
|
||||||
|
// Pseudorandom secret taken directly from FARSH (little-endian)
|
||||||
|
const List<int> _kSecret = <int>[
|
||||||
|
0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, //
|
||||||
|
0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb,
|
||||||
|
0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e,
|
||||||
|
0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
|
||||||
|
0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6,
|
||||||
|
0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb,
|
||||||
|
0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97,
|
||||||
|
0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
|
||||||
|
0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7,
|
||||||
|
0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31,
|
||||||
|
0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83,
|
||||||
|
0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
|
||||||
|
0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26,
|
||||||
|
0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc,
|
||||||
|
0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f,
|
||||||
|
0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from
|
||||||
|
/// https://github.com/RedSpah/xxhash_cpp/blob/master/include/xxhash.hpp
|
||||||
|
class XXH3Sink128bit extends BlockHashSink {
|
||||||
|
final int seed;
|
||||||
|
final int rounds;
|
||||||
|
final Uint8List secret;
|
||||||
|
final Uint64List state = Uint64List(8);
|
||||||
|
final ListQueue<int> last = ListQueue<int>(_midsizeMax);
|
||||||
|
late final Uint64List qbuffer = Uint64List.view(buffer.buffer);
|
||||||
|
late final Uint64List secret64 = Uint64List.view(secret.buffer);
|
||||||
|
late final ByteData secretBD = secret.buffer.asByteData();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 16;
|
||||||
|
|
||||||
|
static const int prime32_1 = 0x9E3779B1;
|
||||||
|
static const int prime32_2 = 0x85EBCA77;
|
||||||
|
static const int prime32_3 = 0xC2B2AE3D;
|
||||||
|
static const int prime64_1 = 0x9E3779B185EBCA87;
|
||||||
|
static const int prime64_2 = 0xC2B2AE3D27D4EB4F;
|
||||||
|
static const int prime64_3 = 0x165667B19E3779F9;
|
||||||
|
static const int prime64_4 = 0x85EBCA77C2B2AE63;
|
||||||
|
static const int prime64_5 = 0x27D4EB2F165667C5;
|
||||||
|
|
||||||
|
factory XXH3Sink128bit.withSeed(int seed) {
|
||||||
|
if (seed == 0) {
|
||||||
|
return XXH3Sink128bit.withSecret();
|
||||||
|
}
|
||||||
|
var secret = Uint8List.fromList(_kSecret);
|
||||||
|
var secret64 = Uint64List.view(secret.buffer);
|
||||||
|
for (int i = 0; i < secret64.length; i += 2) {
|
||||||
|
secret64[i] += seed;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < secret64.length; i += 2) {
|
||||||
|
secret64[i] -= seed;
|
||||||
|
}
|
||||||
|
return XXH3Sink128bit._(
|
||||||
|
seed: seed,
|
||||||
|
secret: secret,
|
||||||
|
rounds: (secret.lengthInBytes - _stripeLen) >>> 3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory XXH3Sink128bit.withSecret([List<int>? secret]) {
|
||||||
|
var key = Uint8List.fromList(secret ?? _kSecret);
|
||||||
|
if (key.lengthInBytes < _minSecretSize) {
|
||||||
|
throw ArgumentError('The secret length must be at least $_minSecretSize');
|
||||||
|
}
|
||||||
|
return XXH3Sink128bit._(
|
||||||
|
seed: 0,
|
||||||
|
secret: key,
|
||||||
|
rounds: (key.lengthInBytes - _stripeLen) >>> 3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH3Sink128bit._({
|
||||||
|
required this.seed,
|
||||||
|
required this.secret,
|
||||||
|
required this.rounds,
|
||||||
|
}) : super(rounds << 6) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
state[0] = prime32_3;
|
||||||
|
state[1] = prime64_1;
|
||||||
|
state[2] = prime64_2;
|
||||||
|
state[3] = prime64_3;
|
||||||
|
state[4] = prime64_4;
|
||||||
|
state[5] = prime32_2;
|
||||||
|
state[6] = prime64_5;
|
||||||
|
state[7] = prime32_1;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
if (last.length == _midsizeMax) {
|
||||||
|
last.removeFirst();
|
||||||
|
}
|
||||||
|
last.add(chunk[start]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _crossSwap(int x) => (x & _mask32) * (x >>> 32);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int n, i, v, l, k;
|
||||||
|
// accumulate
|
||||||
|
for (n = 0; n < rounds; n++) {
|
||||||
|
l = n << 3;
|
||||||
|
for (i = 0; i < state.length; i++) {
|
||||||
|
v = qbuffer[l + i];
|
||||||
|
state[i ^ 1] += v;
|
||||||
|
v ^= secret64[n + i];
|
||||||
|
state[i] += _crossSwap(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scramble
|
||||||
|
k = secret.lengthInBytes - _stripeLen;
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
state[i] ^= state[i] >>> 47;
|
||||||
|
state[i] ^= secretBD.getUint64(k + (i << 3), Endian.little);
|
||||||
|
state[i] *= prime32_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _avalanche(int hash) {
|
||||||
|
hash ^= hash >>> 37;
|
||||||
|
hash *= 0x165667919E3779F9;
|
||||||
|
hash ^= hash >>> 32;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _midsizeAvalanche(int hash) {
|
||||||
|
hash ^= hash >>> 33;
|
||||||
|
hash *= prime64_2;
|
||||||
|
hash ^= hash >>> 29;
|
||||||
|
hash *= prime64_3;
|
||||||
|
hash ^= hash >>> 32;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static Uint8List _combine(int a, int b) {
|
||||||
|
return Uint8List.fromList([
|
||||||
|
b >>> 56,
|
||||||
|
b >>> 48,
|
||||||
|
b >>> 40,
|
||||||
|
b >>> 32,
|
||||||
|
b >>> 24,
|
||||||
|
b >>> 16,
|
||||||
|
b >>> 8,
|
||||||
|
b,
|
||||||
|
a >>> 56,
|
||||||
|
a >>> 48,
|
||||||
|
a >>> 40,
|
||||||
|
a >>> 32,
|
||||||
|
a >>> 24,
|
||||||
|
a >>> 16,
|
||||||
|
a >>> 8,
|
||||||
|
a,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List _finalizeLong(Uint64List stripe) {
|
||||||
|
// XXH3_hashLong_128b
|
||||||
|
int low, high;
|
||||||
|
int t, n, i, v, l, a, b;
|
||||||
|
const int lastAccStart = 7;
|
||||||
|
const int mergeAccStart = 11;
|
||||||
|
|
||||||
|
// accumulate last partial block
|
||||||
|
for (t = n = 0; t + _stripeLen < pos; n++, t += _stripeLen) {
|
||||||
|
l = n << 3;
|
||||||
|
for (i = 0; i < state.length; i++) {
|
||||||
|
v = qbuffer[l + i];
|
||||||
|
state[i ^ 1] += v;
|
||||||
|
v ^= secret64[n + i];
|
||||||
|
state[i] += _crossSwap(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// last stripe
|
||||||
|
t = secret.lengthInBytes - _stripeLen - lastAccStart;
|
||||||
|
for (i = 0; i < state.length; i++, t += 8) {
|
||||||
|
v = stripe[i];
|
||||||
|
state[i ^ 1] += v;
|
||||||
|
v ^= secretBD.getUint64(t, Endian.little);
|
||||||
|
state[i] += _crossSwap(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// converge into final hash
|
||||||
|
low = messageLength * prime64_1;
|
||||||
|
high = ~(messageLength * prime64_2);
|
||||||
|
t = mergeAccStart;
|
||||||
|
for (i = 0; i < 8; i += 2, t += 16) {
|
||||||
|
a = secretBD.getUint64(t, Endian.little);
|
||||||
|
b = secretBD.getUint64(t + 8, Endian.little);
|
||||||
|
low += _mul128fold64(state[i] ^ a, state[i + 1] ^ b);
|
||||||
|
}
|
||||||
|
t = secret.lengthInBytes - _stripeLen - mergeAccStart;
|
||||||
|
for (i = 0; i < 8; i += 2, t += 16) {
|
||||||
|
a = secretBD.getUint64(t, Endian.little);
|
||||||
|
b = secretBD.getUint64(t + 8, Endian.little);
|
||||||
|
high += _mul128fold64(state[i] ^ a, state[i + 1] ^ b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// avalanche
|
||||||
|
return _combine(_avalanche(low), _avalanche(high));
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _swap32(int x) =>
|
||||||
|
((x << 24) & 0xff000000) |
|
||||||
|
((x << 8) & 0x00ff0000) |
|
||||||
|
((x >>> 8) & 0x0000ff00) |
|
||||||
|
((x >>> 24) & 0x000000ff);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _swap64(int x) =>
|
||||||
|
((x << 56) & 0xff00000000000000) |
|
||||||
|
((x << 40) & 0x00ff000000000000) |
|
||||||
|
((x << 24) & 0x0000ff0000000000) |
|
||||||
|
((x << 8) & 0x000000ff00000000) |
|
||||||
|
((x >>> 8) & 0x00000000ff000000) |
|
||||||
|
((x >>> 24) & 0x0000000000ff0000) |
|
||||||
|
((x >>> 40) & 0x000000000000ff00) |
|
||||||
|
((x >>> 56) & 0x00000000000000ff);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
// Multiply two 64-bit numbers to get 128-bit number
|
||||||
|
static void _mul128(int a, int b, Uint64List result) {
|
||||||
|
int al, ah, bl, bh, ll, hl, lh, hh, cross;
|
||||||
|
|
||||||
|
al = a & _mask32;
|
||||||
|
ah = a >>> 32;
|
||||||
|
bl = b & _mask32;
|
||||||
|
bh = b >>> 32;
|
||||||
|
|
||||||
|
ll = al * bl;
|
||||||
|
hl = ah * bl;
|
||||||
|
lh = al * bh;
|
||||||
|
hh = ah * bh;
|
||||||
|
|
||||||
|
cross = (ll >>> 32) + (hl & _mask32) + lh;
|
||||||
|
result[0] = (cross << 32) | (ll & _mask32);
|
||||||
|
result[1] = (hl >>> 32) + (cross >>> 32) + hh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply two 64-bit numbers to get 128-bit number and
|
||||||
|
// xor the low bits of the product with the high bits
|
||||||
|
static int _mul128fold64(int a, int b) {
|
||||||
|
int al, ah, bl, bh, ll, hl, lh, hh, cross, upper, lower;
|
||||||
|
|
||||||
|
al = a & _mask32;
|
||||||
|
ah = a >>> 32;
|
||||||
|
bl = b & _mask32;
|
||||||
|
bh = b >>> 32;
|
||||||
|
|
||||||
|
ll = al * bl;
|
||||||
|
hl = ah * bl;
|
||||||
|
lh = al * bh;
|
||||||
|
hh = ah * bh;
|
||||||
|
|
||||||
|
cross = (ll >>> 32) + (hl & _mask32) + lh;
|
||||||
|
upper = (hl >>> 32) + (cross >>> 32) + hh;
|
||||||
|
lower = (cross << 32) | (ll & _mask32);
|
||||||
|
|
||||||
|
return upper ^ lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _mix16B(ByteData input, int i, ByteData key, int j, int seed) {
|
||||||
|
int lhs, rhs;
|
||||||
|
lhs = key.getUint64(j, Endian.little) + seed;
|
||||||
|
rhs = key.getUint64(j + 8, Endian.little) - seed;
|
||||||
|
lhs ^= input.getUint64(i, Endian.little);
|
||||||
|
rhs ^= input.getUint64(i + 8, Endian.little);
|
||||||
|
return _mul128fold64(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _mix32B(
|
||||||
|
Uint64List acc,
|
||||||
|
ByteData input,
|
||||||
|
int a,
|
||||||
|
int b,
|
||||||
|
ByteData key,
|
||||||
|
int c,
|
||||||
|
int seed,
|
||||||
|
) {
|
||||||
|
acc[0] += _mix16B(input, a, key, c, seed);
|
||||||
|
acc[0] ^= input.getUint64(b, Endian.little) +
|
||||||
|
input.getUint64(b + 8, Endian.little);
|
||||||
|
acc[1] += _mix16B(input, b, key, c + 16, seed);
|
||||||
|
acc[1] ^= input.getUint64(a, Endian.little) +
|
||||||
|
input.getUint64(a + 8, Endian.little);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List _finalizeShort(ByteData input, int length, ByteData key) {
|
||||||
|
int low, high;
|
||||||
|
int i, lhs, rhs, a, b, c, x, y;
|
||||||
|
Uint64List acc = Uint64List(2);
|
||||||
|
if (length == 0) {
|
||||||
|
// hash_t<N> len_0to16
|
||||||
|
low = seed;
|
||||||
|
low ^= key.getUint64(64, Endian.little);
|
||||||
|
low ^= key.getUint64(72, Endian.little);
|
||||||
|
|
||||||
|
high = seed;
|
||||||
|
high ^= key.getUint64(80, Endian.little);
|
||||||
|
high ^= key.getUint64(88, Endian.little);
|
||||||
|
|
||||||
|
low = _midsizeAvalanche(low);
|
||||||
|
high = _midsizeAvalanche(high);
|
||||||
|
} else if (length <= 3) {
|
||||||
|
// hash_t<N> len_1to3
|
||||||
|
a = input.getUint8(0);
|
||||||
|
b = input.getUint8(length >>> 1);
|
||||||
|
c = input.getUint8(length - 1);
|
||||||
|
x = (a << 16) | (b << 24) | (c) | (length << 8);
|
||||||
|
y = _rotl32(_swap32(x), 13);
|
||||||
|
|
||||||
|
low = key.getUint32(0, Endian.little);
|
||||||
|
low ^= key.getUint32(4, Endian.little);
|
||||||
|
low += seed;
|
||||||
|
low ^= x;
|
||||||
|
|
||||||
|
high = key.getUint32(8, Endian.little);
|
||||||
|
high ^= key.getUint32(12, Endian.little);
|
||||||
|
high -= seed;
|
||||||
|
high ^= y;
|
||||||
|
|
||||||
|
low = _midsizeAvalanche(low);
|
||||||
|
high = _midsizeAvalanche(high);
|
||||||
|
} else if (length <= 8) {
|
||||||
|
// hash_t<N> len_4to8
|
||||||
|
lhs = input.getUint32(0, Endian.little);
|
||||||
|
rhs = input.getUint32(length - 4, Endian.little);
|
||||||
|
|
||||||
|
x = key.getUint64(16, Endian.little);
|
||||||
|
x ^= key.getUint64(24, Endian.little);
|
||||||
|
x += seed ^ (_swap32(seed & _mask32) << 32);
|
||||||
|
x ^= (rhs << 32) | lhs;
|
||||||
|
|
||||||
|
_mul128(x, prime64_1 + (length << 2), acc);
|
||||||
|
low = acc[0];
|
||||||
|
high = acc[1];
|
||||||
|
|
||||||
|
high += low << 1;
|
||||||
|
low ^= high >>> 3;
|
||||||
|
low ^= low >>> 35;
|
||||||
|
low *= 0x9FB21C651E98DF25;
|
||||||
|
low ^= low >>> 28;
|
||||||
|
|
||||||
|
high = _avalanche(high);
|
||||||
|
} else if (length <= 16) {
|
||||||
|
// hash_t<N> len_9to16
|
||||||
|
lhs = key.getUint64(32, Endian.little);
|
||||||
|
lhs ^= key.getUint64(40, Endian.little);
|
||||||
|
lhs -= seed;
|
||||||
|
lhs ^= input.getUint64(0, Endian.little);
|
||||||
|
lhs ^= input.getUint64(length - 8, Endian.little);
|
||||||
|
|
||||||
|
rhs = key.getUint64(48, Endian.little);
|
||||||
|
rhs ^= key.getUint64(56, Endian.little);
|
||||||
|
rhs += seed;
|
||||||
|
rhs ^= input.getUint64(length - 8, Endian.little);
|
||||||
|
|
||||||
|
_mul128(lhs, prime64_1, acc);
|
||||||
|
low = acc[0];
|
||||||
|
high = acc[1];
|
||||||
|
low += length - 1 << 54;
|
||||||
|
high += (rhs & (_mask32 << 32)) + ((rhs & _mask32) * prime32_2);
|
||||||
|
|
||||||
|
low ^= _swap64(high);
|
||||||
|
_mul128(low, prime64_2, acc);
|
||||||
|
low = acc[0];
|
||||||
|
high = acc[1] + (high * prime64_2);
|
||||||
|
|
||||||
|
low = _avalanche(low);
|
||||||
|
high = _avalanche(high);
|
||||||
|
} else if (length <= 128) {
|
||||||
|
// hash_t<N> len_17to128
|
||||||
|
acc[0] = length * prime64_1;
|
||||||
|
acc[1] = 0;
|
||||||
|
if (length > 32) {
|
||||||
|
if (length > 64) {
|
||||||
|
if (length > 96) {
|
||||||
|
_mix32B(acc, input, 48, length - 64, key, 96, seed);
|
||||||
|
}
|
||||||
|
_mix32B(acc, input, 32, length - 48, key, 64, seed);
|
||||||
|
}
|
||||||
|
_mix32B(acc, input, 16, length - 32, key, 32, seed);
|
||||||
|
}
|
||||||
|
_mix32B(acc, input, 0, length - 16, key, 0, seed);
|
||||||
|
|
||||||
|
low = acc[0] + acc[1];
|
||||||
|
high = acc[0] * prime64_1 + acc[1] * prime64_4;
|
||||||
|
high += (length - seed) * prime64_2;
|
||||||
|
low = _avalanche(low);
|
||||||
|
high = -_avalanche(high);
|
||||||
|
} else {
|
||||||
|
// hash_t<N> len_129to240
|
||||||
|
const int startOffset = 3;
|
||||||
|
const int lastOffset = 17;
|
||||||
|
acc[0] = length * prime64_1;
|
||||||
|
acc[1] = 0;
|
||||||
|
// first 128 bytes
|
||||||
|
for (i = 0; i < 128; i += 32) {
|
||||||
|
_mix32B(
|
||||||
|
acc,
|
||||||
|
input,
|
||||||
|
i,
|
||||||
|
i + 16,
|
||||||
|
key,
|
||||||
|
i,
|
||||||
|
seed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
acc[0] = _avalanche(acc[0]);
|
||||||
|
acc[1] = _avalanche(acc[1]);
|
||||||
|
// remaining bytes
|
||||||
|
for (i = 128; i + 32 <= length; i += 32) {
|
||||||
|
_mix32B(
|
||||||
|
acc,
|
||||||
|
input,
|
||||||
|
i,
|
||||||
|
i + 16,
|
||||||
|
key,
|
||||||
|
startOffset + i - 128,
|
||||||
|
seed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// last byte
|
||||||
|
_mix32B(
|
||||||
|
acc,
|
||||||
|
input,
|
||||||
|
length - 16,
|
||||||
|
length - 32,
|
||||||
|
key,
|
||||||
|
_minSecretSize - lastOffset - 16,
|
||||||
|
-seed,
|
||||||
|
);
|
||||||
|
// mid-range avalanche
|
||||||
|
low = _avalanche(acc[0] + acc[1]);
|
||||||
|
high = acc[1] * prime64_4;
|
||||||
|
high += acc[0] * prime64_1;
|
||||||
|
high += (length - seed) * prime64_2;
|
||||||
|
high = -_avalanche(high);
|
||||||
|
}
|
||||||
|
// combine
|
||||||
|
return _combine(low, high);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
int i;
|
||||||
|
ByteData key;
|
||||||
|
Uint64List input = Uint64List(_midsizeMax >>> 3);
|
||||||
|
Uint8List input8 = Uint8List.view(input.buffer);
|
||||||
|
|
||||||
|
if (messageLength <= _midsizeMax) {
|
||||||
|
var it = last.iterator;
|
||||||
|
for (i = 0; it.moveNext(); ++i) {
|
||||||
|
input8[i] = it.current;
|
||||||
|
}
|
||||||
|
if (seed == 0) {
|
||||||
|
key = secretBD;
|
||||||
|
} else {
|
||||||
|
key = Uint8List.fromList(_kSecret).buffer.asByteData();
|
||||||
|
}
|
||||||
|
return _finalizeShort(input.buffer.asByteData(), i, key);
|
||||||
|
} else {
|
||||||
|
for (i = _stripeLen - 1; i >= 0; --i) {
|
||||||
|
input8[i] = last.removeLast();
|
||||||
|
}
|
||||||
|
return _finalizeLong(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
hashlib/lib/src/algorithms/xxh3/xxh3_64.dart
Normal file
4
hashlib/lib/src/algorithms/xxh3/xxh3_64.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
export 'xxh3_64_64bit.dart' if (dart.library.js) 'xxh3_64_32bit.dart';
|
||||||
32
hashlib/lib/src/algorithms/xxh3/xxh3_64_32bit.dart
Normal file
32
hashlib/lib/src/algorithms/xxh3/xxh3_64_32bit.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
/// Stub for 32-bit machines.
|
||||||
|
class XXH3Sink64bit extends BlockHashSink {
|
||||||
|
@override
|
||||||
|
final int hashLength = 8;
|
||||||
|
|
||||||
|
factory XXH3Sink64bit.withSeed(int seed) {
|
||||||
|
seed;
|
||||||
|
throw UnimplementedError('XXH3 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
factory XXH3Sink64bit.withSecret([List<int>? secret]) {
|
||||||
|
secret;
|
||||||
|
throw UnimplementedError('XXH3 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
throw UnimplementedError('XXH3 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
throw UnimplementedError('XXH3 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
}
|
||||||
391
hashlib/lib/src/algorithms/xxh3/xxh3_64_64bit.dart
Normal file
391
hashlib/lib/src/algorithms/xxh3/xxh3_64_64bit.dart
Normal file
|
|
@ -0,0 +1,391 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:collection';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const int _stripeLen = 64;
|
||||||
|
const int _midsizeMax = 240;
|
||||||
|
const int _minSecretSize = 136;
|
||||||
|
|
||||||
|
// Pseudorandom secret taken directly from FARSH (little-endian)
|
||||||
|
const List<int> _kSecret = <int>[
|
||||||
|
0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, //
|
||||||
|
0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb,
|
||||||
|
0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e,
|
||||||
|
0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
|
||||||
|
0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6,
|
||||||
|
0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb,
|
||||||
|
0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97,
|
||||||
|
0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
|
||||||
|
0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7,
|
||||||
|
0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31,
|
||||||
|
0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83,
|
||||||
|
0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
|
||||||
|
0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26,
|
||||||
|
0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc,
|
||||||
|
0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f,
|
||||||
|
0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This implementation is derived from
|
||||||
|
/// https://github.com/RedSpah/xxhash_cpp/blob/master/include/xxhash.hpp
|
||||||
|
class XXH3Sink64bit extends BlockHashSink {
|
||||||
|
final int seed;
|
||||||
|
final int rounds;
|
||||||
|
final Uint8List secret;
|
||||||
|
final Uint64List acc = Uint64List(8);
|
||||||
|
final ListQueue<int> last = ListQueue<int>(_midsizeMax);
|
||||||
|
late final Uint64List qbuffer = Uint64List.view(buffer.buffer);
|
||||||
|
late final Uint64List secret64 = Uint64List.view(secret.buffer);
|
||||||
|
late final ByteData secretBD = secret.buffer.asByteData();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 8;
|
||||||
|
|
||||||
|
static const int prime32_1 = 0x9E3779B1;
|
||||||
|
static const int prime32_2 = 0x85EBCA77;
|
||||||
|
static const int prime32_3 = 0xC2B2AE3D;
|
||||||
|
static const int prime64_1 = 0x9E3779B185EBCA87;
|
||||||
|
static const int prime64_2 = 0xC2B2AE3D27D4EB4F;
|
||||||
|
static const int prime64_3 = 0x165667B19E3779F9;
|
||||||
|
static const int prime64_4 = 0x85EBCA77C2B2AE63;
|
||||||
|
static const int prime64_5 = 0x27D4EB2F165667C5;
|
||||||
|
|
||||||
|
factory XXH3Sink64bit.withSeed(int seed) {
|
||||||
|
if (seed == 0) {
|
||||||
|
return XXH3Sink64bit.withSecret();
|
||||||
|
}
|
||||||
|
var secret = Uint8List.fromList(_kSecret);
|
||||||
|
var secret64 = Uint64List.view(secret.buffer);
|
||||||
|
for (int i = 0; i < secret64.length; i += 2) {
|
||||||
|
secret64[i] += seed;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < secret64.length; i += 2) {
|
||||||
|
secret64[i] -= seed;
|
||||||
|
}
|
||||||
|
return XXH3Sink64bit._(
|
||||||
|
seed: seed,
|
||||||
|
secret: secret,
|
||||||
|
rounds: (secret.lengthInBytes - _stripeLen) >>> 3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory XXH3Sink64bit.withSecret([List<int>? secret]) {
|
||||||
|
var key = Uint8List.fromList(secret ?? _kSecret);
|
||||||
|
if (key.lengthInBytes < _minSecretSize) {
|
||||||
|
throw ArgumentError('The secret length must be at least $_minSecretSize');
|
||||||
|
}
|
||||||
|
return XXH3Sink64bit._(
|
||||||
|
seed: 0,
|
||||||
|
secret: key,
|
||||||
|
rounds: (key.lengthInBytes - _stripeLen) >>> 3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH3Sink64bit._({
|
||||||
|
required this.seed,
|
||||||
|
required this.secret,
|
||||||
|
required this.rounds,
|
||||||
|
}) : super(rounds << 6) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
acc[0] = prime32_3;
|
||||||
|
acc[1] = prime64_1;
|
||||||
|
acc[2] = prime64_2;
|
||||||
|
acc[3] = prime64_3;
|
||||||
|
acc[4] = prime64_4;
|
||||||
|
acc[5] = prime32_2;
|
||||||
|
acc[6] = prime64_5;
|
||||||
|
acc[7] = prime32_1;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
if (last.length == _midsizeMax) {
|
||||||
|
last.removeFirst();
|
||||||
|
}
|
||||||
|
last.add(chunk[start]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _crossSwap(int x) => (x & _mask32) * (x >>> 32);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
int n, i, v, l, k;
|
||||||
|
// accumulate
|
||||||
|
for (n = 0; n < rounds; n++) {
|
||||||
|
l = n << 3;
|
||||||
|
for (i = 0; i < acc.length; i++) {
|
||||||
|
v = qbuffer[l + i];
|
||||||
|
acc[i ^ 1] += v;
|
||||||
|
v ^= secret64[n + i];
|
||||||
|
acc[i] += _crossSwap(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scramble
|
||||||
|
k = secret.lengthInBytes - _stripeLen;
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
acc[i] ^= acc[i] >>> 47;
|
||||||
|
acc[i] ^= secretBD.getUint64(k + (i << 3), Endian.little);
|
||||||
|
acc[i] *= prime32_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _avalanche(int hash) {
|
||||||
|
hash ^= hash >>> 37;
|
||||||
|
hash *= 0x165667919E3779F9;
|
||||||
|
hash ^= hash >>> 32;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _midsizeAvalanche(int hash) {
|
||||||
|
hash ^= hash >>> 33;
|
||||||
|
hash *= prime64_2;
|
||||||
|
hash ^= hash >>> 29;
|
||||||
|
hash *= prime64_3;
|
||||||
|
hash ^= hash >>> 32;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rrmxmx(int hash, int length) {
|
||||||
|
hash ^= _rotl64(hash, 49) ^ _rotl64(hash, 24);
|
||||||
|
hash *= 0x9FB21C651E98DF25;
|
||||||
|
hash ^= (hash >>> 35) + length;
|
||||||
|
hash *= 0x9FB21C651E98DF25;
|
||||||
|
hash ^= hash >>> 28;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _finalizeLong(Uint64List stripe) {
|
||||||
|
// void hash_long_internal_loop
|
||||||
|
int hash;
|
||||||
|
int t, n, i, v, l, a, b;
|
||||||
|
const int lastAccStart = 7;
|
||||||
|
const int mergeAccStart = 11;
|
||||||
|
|
||||||
|
// last partial block
|
||||||
|
for (t = n = 0; t + _stripeLen < pos; n++, t += _stripeLen) {
|
||||||
|
l = n << 3;
|
||||||
|
for (i = 0; i < acc.length; i++) {
|
||||||
|
v = qbuffer[l + i];
|
||||||
|
acc[i ^ 1] += v;
|
||||||
|
v ^= secret64[n + i];
|
||||||
|
acc[i] += _crossSwap(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// last stripe
|
||||||
|
t = secret.lengthInBytes - _stripeLen - lastAccStart;
|
||||||
|
for (i = 0; i < acc.length; i++, t += 8) {
|
||||||
|
v = stripe[i];
|
||||||
|
acc[i ^ 1] += v;
|
||||||
|
v ^= secretBD.getUint64(t, Endian.little);
|
||||||
|
acc[i] += _crossSwap(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// converge into final hash: uint64_t merge_accs
|
||||||
|
hash = messageLength * prime64_1;
|
||||||
|
t = mergeAccStart;
|
||||||
|
for (i = 0; i < 8; i += 2, t += 16) {
|
||||||
|
a = acc[i] ^ secretBD.getUint64(t, Endian.little);
|
||||||
|
b = acc[i + 1] ^ secretBD.getUint64(t + 8, Endian.little);
|
||||||
|
hash += _mul128fold64(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// avalanche
|
||||||
|
return _avalanche(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _swap32(int x) =>
|
||||||
|
((x << 24) & 0xff000000) |
|
||||||
|
((x << 8) & 0x00ff0000) |
|
||||||
|
((x >>> 8) & 0x0000ff00) |
|
||||||
|
((x >>> 24) & 0x000000ff);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _swap64(int x) =>
|
||||||
|
((x << 56) & 0xff00000000000000) |
|
||||||
|
((x << 40) & 0x00ff000000000000) |
|
||||||
|
((x << 24) & 0x0000ff0000000000) |
|
||||||
|
((x << 8) & 0x000000ff00000000) |
|
||||||
|
((x >>> 8) & 0x00000000ff000000) |
|
||||||
|
((x >>> 24) & 0x0000000000ff0000) |
|
||||||
|
((x >>> 40) & 0x000000000000ff00) |
|
||||||
|
((x >>> 56) & 0x00000000000000ff);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl64(int x, int n) => (x << n) | (x >>> (64 - n));
|
||||||
|
|
||||||
|
// Multiply two 64-bit numbers to get 128-bit number and
|
||||||
|
// xor the low bits of the product with the high bits
|
||||||
|
static int _mul128fold64(int a, int b) {
|
||||||
|
int al, ah, bl, bh, ll, hl, lh, hh, cross, upper, lower;
|
||||||
|
|
||||||
|
al = a & _mask32;
|
||||||
|
ah = a >>> 32;
|
||||||
|
bl = b & _mask32;
|
||||||
|
bh = b >>> 32;
|
||||||
|
|
||||||
|
ll = al * bl;
|
||||||
|
hl = ah * bl;
|
||||||
|
lh = al * bh;
|
||||||
|
hh = ah * bh;
|
||||||
|
|
||||||
|
cross = (ll >>> 32) + (hl & _mask32) + lh;
|
||||||
|
upper = (hl >>> 32) + (cross >>> 32) + hh;
|
||||||
|
lower = (cross << 32) | (ll & _mask32);
|
||||||
|
|
||||||
|
return upper ^ lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _mix16B(ByteData input, int i, ByteData key, int j, int seed) {
|
||||||
|
int lhs, rhs;
|
||||||
|
lhs = input.getUint64(i, Endian.little);
|
||||||
|
rhs = input.getUint64(i + 8, Endian.little);
|
||||||
|
lhs ^= key.getUint64(j, Endian.little) + seed;
|
||||||
|
rhs ^= key.getUint64(j + 8, Endian.little) - seed;
|
||||||
|
return _mul128fold64(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _finalizeShort(ByteData input, int length, ByteData key) {
|
||||||
|
int hash, lhs, rhs, a, b, c, i;
|
||||||
|
if (length == 0) {
|
||||||
|
// hash_t<N> len_0to16
|
||||||
|
hash = seed;
|
||||||
|
hash ^= key.getUint64(56, Endian.little);
|
||||||
|
hash ^= key.getUint64(64, Endian.little);
|
||||||
|
return _midsizeAvalanche(hash);
|
||||||
|
} else if (length <= 3) {
|
||||||
|
// hash_t<N> len_1to3
|
||||||
|
a = input.getUint8(0);
|
||||||
|
b = input.getUint8(length >>> 1);
|
||||||
|
c = input.getUint8(length - 1);
|
||||||
|
hash = key.getUint32(0, Endian.little);
|
||||||
|
hash ^= key.getUint32(4, Endian.little);
|
||||||
|
hash += seed;
|
||||||
|
hash ^= (a << 16) | (b << 24) | (c) | (length << 8);
|
||||||
|
return _midsizeAvalanche(hash);
|
||||||
|
} else if (length <= 8) {
|
||||||
|
// hash_t<N> len_4to8
|
||||||
|
lhs = input.getUint32(0, Endian.little);
|
||||||
|
rhs = input.getUint32(length - 4, Endian.little);
|
||||||
|
hash = key.getUint64(8, Endian.little);
|
||||||
|
hash ^= key.getUint64(16, Endian.little);
|
||||||
|
hash -= seed ^ ((_swap32(seed) & _mask32) << 32);
|
||||||
|
hash ^= (lhs << 32) | rhs;
|
||||||
|
return _rrmxmx(hash, length);
|
||||||
|
} else if (length <= 16) {
|
||||||
|
// hash_t<N> len_9to16
|
||||||
|
lhs = key.getUint64(24, Endian.little);
|
||||||
|
lhs ^= key.getUint64(32, Endian.little);
|
||||||
|
lhs += seed;
|
||||||
|
|
||||||
|
rhs = key.getUint64(40, Endian.little);
|
||||||
|
rhs ^= key.getUint64(48, Endian.little);
|
||||||
|
rhs -= seed;
|
||||||
|
|
||||||
|
lhs ^= input.getUint64(0, Endian.little);
|
||||||
|
rhs ^= input.getUint64(length - 8, Endian.little);
|
||||||
|
|
||||||
|
hash = length + _swap64(lhs) + rhs + _mul128fold64(lhs, rhs);
|
||||||
|
return _avalanche(hash);
|
||||||
|
} else if (length <= 128) {
|
||||||
|
// hash_t<N> len_17to128
|
||||||
|
hash = length * prime64_1;
|
||||||
|
if (length > 32) {
|
||||||
|
if (length > 64) {
|
||||||
|
if (length > 96) {
|
||||||
|
hash += _mix16B(input, 48, key, 96, seed);
|
||||||
|
hash += _mix16B(input, length - 64, key, 112, seed);
|
||||||
|
}
|
||||||
|
hash += _mix16B(input, 32, key, 64, seed);
|
||||||
|
hash += _mix16B(input, length - 48, key, 80, seed);
|
||||||
|
}
|
||||||
|
hash += _mix16B(input, 16, key, 32, seed);
|
||||||
|
hash += _mix16B(input, length - 32, key, 48, seed);
|
||||||
|
}
|
||||||
|
hash += _mix16B(input, 0, key, 0, seed);
|
||||||
|
hash += _mix16B(input, length - 16, key, 16, seed);
|
||||||
|
return _avalanche(hash);
|
||||||
|
} else {
|
||||||
|
// hash_t<N> len_129to240
|
||||||
|
const int startOffset = 3;
|
||||||
|
const int lastOffset = 17;
|
||||||
|
hash = length * prime64_1;
|
||||||
|
// first 128 bytes
|
||||||
|
for (i = 0; i < 128; i += 16) {
|
||||||
|
hash += _mix16B(input, i, key, i, seed);
|
||||||
|
}
|
||||||
|
hash = _avalanche(hash);
|
||||||
|
// remaining bytes
|
||||||
|
for (i = 128; i + 16 <= length; i += 16) {
|
||||||
|
c = startOffset + i - 128;
|
||||||
|
hash += _mix16B(input, i, key, c, seed);
|
||||||
|
}
|
||||||
|
// last byte
|
||||||
|
c = _minSecretSize - lastOffset;
|
||||||
|
hash += _mix16B(input, length - 16, key, c, seed);
|
||||||
|
return _avalanche(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
int i;
|
||||||
|
int hash;
|
||||||
|
ByteData key;
|
||||||
|
Uint64List input = Uint64List(_midsizeMax >>> 3);
|
||||||
|
Uint8List input8 = Uint8List.view(input.buffer);
|
||||||
|
|
||||||
|
if (messageLength <= _midsizeMax) {
|
||||||
|
var it = last.iterator;
|
||||||
|
for (i = 0; it.moveNext(); ++i) {
|
||||||
|
input8[i] = it.current;
|
||||||
|
}
|
||||||
|
if (seed == 0) {
|
||||||
|
key = secretBD;
|
||||||
|
} else {
|
||||||
|
key = Uint8List.fromList(_kSecret).buffer.asByteData();
|
||||||
|
}
|
||||||
|
hash = _finalizeShort(input.buffer.asByteData(), i, key);
|
||||||
|
} else {
|
||||||
|
for (i = 63; i >= 0; --i) {
|
||||||
|
input8[i] = last.removeLast();
|
||||||
|
}
|
||||||
|
hash = _finalizeLong(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uint8List.fromList([
|
||||||
|
hash >>> 56,
|
||||||
|
hash >>> 48,
|
||||||
|
hash >>> 40,
|
||||||
|
hash >>> 32,
|
||||||
|
hash >>> 24,
|
||||||
|
hash >>> 16,
|
||||||
|
hash >>> 8,
|
||||||
|
hash,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
hashlib/lib/src/algorithms/xxh32/xxh32.dart
Normal file
4
hashlib/lib/src/algorithms/xxh32/xxh32.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
export 'xxh32_64bit.dart' if (dart.library.js) 'xxh32_32bit.dart';
|
||||||
136
hashlib/lib/src/algorithms/xxh32/xxh32_32bit.dart
Normal file
136
hashlib/lib/src/algorithms/xxh32/xxh32_32bit.dart
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask16 = 0xFFFF;
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
/// This implementation is derived from
|
||||||
|
/// https://github.com/easyaspi314/xxhash-clean/blob/master/xxhash32-ref.c
|
||||||
|
class XXHash32Sink extends BlockHashSink {
|
||||||
|
final int seed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 4;
|
||||||
|
|
||||||
|
static const int prime32_1 = 0x9E3779B1;
|
||||||
|
static const int prime32_2 = 0x85EBCA77;
|
||||||
|
static const int prime32_3 = 0xC2B2AE3D;
|
||||||
|
static const int prime32_4 = 0x27D4EB2F;
|
||||||
|
static const int prime32_5 = 0x165667B1;
|
||||||
|
|
||||||
|
int _acc1 = 0;
|
||||||
|
int _acc2 = 0;
|
||||||
|
int _acc3 = 0;
|
||||||
|
int _acc4 = 0;
|
||||||
|
|
||||||
|
XXHash32Sink(this.seed) : super(16) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_acc1 = (seed & _mask32) + prime32_1 + prime32_2;
|
||||||
|
_acc2 = (seed & _mask32) + prime32_2;
|
||||||
|
_acc3 = (seed & _mask32) + 0;
|
||||||
|
_acc4 = (seed & _mask32) - prime32_1;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns ([a] * [b]) within 32-bit Galois field
|
||||||
|
static int cross32(int a, int b) {
|
||||||
|
int ah = (a >>> 16) & _mask16;
|
||||||
|
int al = a & _mask16;
|
||||||
|
int bh = (b >>> 16) & _mask16;
|
||||||
|
int bl = b & _mask16;
|
||||||
|
a = (ah * bl + al * bh) & _mask32;
|
||||||
|
b = (al * bl) & _mask32;
|
||||||
|
return ((a << 16) + b) & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotates 32-bit number [x] by [n] bits
|
||||||
|
static int rotl32(int x, int n) =>
|
||||||
|
((x << n) & _mask32) | ((x & _mask32) >>> (32 - n));
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
_acc1 += cross32(sbuffer[0], prime32_2);
|
||||||
|
_acc1 = rotl32(_acc1, 13);
|
||||||
|
_acc1 = cross32(_acc1, prime32_1);
|
||||||
|
|
||||||
|
_acc2 += cross32(sbuffer[1], prime32_2);
|
||||||
|
_acc2 = rotl32(_acc2, 13);
|
||||||
|
_acc2 = cross32(_acc2, prime32_1);
|
||||||
|
|
||||||
|
_acc3 += cross32(sbuffer[2], prime32_2);
|
||||||
|
_acc3 = rotl32(_acc3, 13);
|
||||||
|
_acc3 = cross32(_acc3, prime32_1);
|
||||||
|
|
||||||
|
_acc4 += cross32(sbuffer[3], prime32_2);
|
||||||
|
_acc4 = rotl32(_acc4, 13);
|
||||||
|
_acc4 = cross32(_acc4, prime32_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
int i, t;
|
||||||
|
int hash;
|
||||||
|
|
||||||
|
if (messageLength < 16) {
|
||||||
|
hash = (seed & _mask32) + prime32_5;
|
||||||
|
} else {
|
||||||
|
hash = rotl32(_acc1, 1);
|
||||||
|
hash += rotl32(_acc2, 7);
|
||||||
|
hash += rotl32(_acc3, 12);
|
||||||
|
hash += rotl32(_acc4, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash += messageLength & _mask32;
|
||||||
|
|
||||||
|
// process the remaining data
|
||||||
|
for (i = t = 0; t + 4 <= pos; ++i, t += 4) {
|
||||||
|
hash += cross32(sbuffer[i], prime32_3);
|
||||||
|
hash = rotl32(hash, 17);
|
||||||
|
hash = cross32(hash, prime32_4);
|
||||||
|
}
|
||||||
|
for (; t < pos; t++) {
|
||||||
|
hash += cross32(buffer[t], prime32_5);
|
||||||
|
hash = rotl32(hash, 11);
|
||||||
|
hash = cross32(hash, prime32_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// avalanche
|
||||||
|
hash &= _mask32;
|
||||||
|
hash ^= hash >>> 15;
|
||||||
|
hash = cross32(hash, prime32_2);
|
||||||
|
hash ^= hash >>> 13;
|
||||||
|
hash = cross32(hash, prime32_3);
|
||||||
|
hash ^= hash >>> 16;
|
||||||
|
|
||||||
|
return Uint8List.fromList([
|
||||||
|
hash >>> 24,
|
||||||
|
hash >>> 16,
|
||||||
|
hash >>> 8,
|
||||||
|
hash,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
119
hashlib/lib/src/algorithms/xxh32/xxh32_64bit.dart
Normal file
119
hashlib/lib/src/algorithms/xxh32/xxh32_64bit.dart
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
const int _mask32 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
/// This implementation is derived from
|
||||||
|
/// https://github.com/easyaspi314/xxhash-clean/blob/master/xxhash32-ref.c
|
||||||
|
class XXHash32Sink extends BlockHashSink {
|
||||||
|
final int seed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 4;
|
||||||
|
|
||||||
|
static const int prime32_1 = 0x9E3779B1;
|
||||||
|
static const int prime32_2 = 0x85EBCA77;
|
||||||
|
static const int prime32_3 = 0xC2B2AE3D;
|
||||||
|
static const int prime32_4 = 0x27D4EB2F;
|
||||||
|
static const int prime32_5 = 0x165667B1;
|
||||||
|
|
||||||
|
int _acc1 = 0;
|
||||||
|
int _acc2 = 0;
|
||||||
|
int _acc3 = 0;
|
||||||
|
int _acc4 = 0;
|
||||||
|
|
||||||
|
XXHash32Sink(this.seed) : super(16) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_acc1 = (seed & _mask32) + prime32_1 + prime32_2;
|
||||||
|
_acc2 = (seed & _mask32) + prime32_2;
|
||||||
|
_acc3 = (seed & _mask32) + 0;
|
||||||
|
_acc4 = (seed & _mask32) - prime32_1;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
_acc1 = (_acc1 + sbuffer[0] * prime32_2) & _mask32;
|
||||||
|
_acc1 = (_acc1 << 13) | (_acc1 >>> 19);
|
||||||
|
_acc1 = (_acc1 * prime32_1) & _mask32;
|
||||||
|
|
||||||
|
_acc2 = (_acc2 + sbuffer[1] * prime32_2) & _mask32;
|
||||||
|
_acc2 = (_acc2 << 13) | (_acc2 >>> 19);
|
||||||
|
_acc2 = (_acc2 * prime32_1) & _mask32;
|
||||||
|
|
||||||
|
_acc3 = (_acc3 + sbuffer[2] * prime32_2) & _mask32;
|
||||||
|
_acc3 = (_acc3 << 13) | (_acc3 >>> 19);
|
||||||
|
_acc3 = (_acc3 * prime32_1) & _mask32;
|
||||||
|
|
||||||
|
_acc4 = (_acc4 + sbuffer[3] * prime32_2) & _mask32;
|
||||||
|
_acc4 = (_acc4 << 13) | (_acc4 >>> 19);
|
||||||
|
_acc4 = (_acc4 * prime32_1) & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
int i, t;
|
||||||
|
int hash;
|
||||||
|
|
||||||
|
if (messageLength < 16) {
|
||||||
|
hash = (seed & _mask32) + prime32_5;
|
||||||
|
} else {
|
||||||
|
hash = (_acc1 << 1) | (_acc1 >>> 31);
|
||||||
|
hash += (_acc2 << 7) | (_acc2 >>> 25);
|
||||||
|
hash += (_acc3 << 12) | (_acc3 >>> 20);
|
||||||
|
hash += (_acc4 << 18) | (_acc4 >>> 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = (hash + messageLength) & _mask32;
|
||||||
|
|
||||||
|
// process the remaining data
|
||||||
|
for (i = t = 0; t + 4 <= pos; ++i, t += 4) {
|
||||||
|
hash = (hash + sbuffer[i] * prime32_3) & _mask32;
|
||||||
|
hash = (hash << 17) | (hash >>> 15);
|
||||||
|
hash = (hash * prime32_4) & _mask32;
|
||||||
|
}
|
||||||
|
for (; t < pos; t++) {
|
||||||
|
hash = (hash + buffer[t] * prime32_5) & _mask32;
|
||||||
|
hash = (hash << 11) | (hash >>> 21);
|
||||||
|
hash = (hash * prime32_1) & _mask32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// avalanche
|
||||||
|
hash ^= hash >>> 15;
|
||||||
|
hash = (hash * prime32_2) & _mask32;
|
||||||
|
hash ^= hash >>> 13;
|
||||||
|
hash = (hash * prime32_3) & _mask32;
|
||||||
|
hash ^= hash >>> 16;
|
||||||
|
|
||||||
|
return Uint8List.fromList([
|
||||||
|
hash >>> 24,
|
||||||
|
hash >>> 16,
|
||||||
|
hash >>> 8,
|
||||||
|
hash,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
hashlib/lib/src/algorithms/xxh64/xxh64.dart
Normal file
4
hashlib/lib/src/algorithms/xxh64/xxh64.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
export 'xxh64_64bit.dart' if (dart.library.js) 'xxh64_32bit.dart';
|
||||||
27
hashlib/lib/src/algorithms/xxh64/xxh64_32bit.dart
Normal file
27
hashlib/lib/src/algorithms/xxh64/xxh64_32bit.dart
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
/// Stub for 32-bit machines.
|
||||||
|
class XXHash64Sink extends BlockHashSink {
|
||||||
|
@override
|
||||||
|
final int hashLength = 8;
|
||||||
|
|
||||||
|
XXHash64Sink(int seed) : super(32) {
|
||||||
|
seed;
|
||||||
|
throw UnimplementedError('XXHash64 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
throw UnimplementedError('XXHash64 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
throw UnimplementedError('XXHash64 is not supported in Node platform');
|
||||||
|
}
|
||||||
|
}
|
||||||
137
hashlib/lib/src/algorithms/xxh64/xxh64_64bit.dart
Normal file
137
hashlib/lib/src/algorithms/xxh64/xxh64_64bit.dart
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
|
||||||
|
/// This implementation is derived from
|
||||||
|
/// https://github.com/easyaspi314/xxhash-clean/blob/master/xxhash64-ref.c
|
||||||
|
class XXHash64Sink extends BlockHashSink {
|
||||||
|
final int seed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int hashLength = 8;
|
||||||
|
|
||||||
|
static const int prime64_1 = 0x9E3779B185EBCA87;
|
||||||
|
static const int prime64_2 = 0xC2B2AE3D27D4EB4F;
|
||||||
|
static const int prime64_3 = 0x165667B19E3779F9;
|
||||||
|
static const int prime64_4 = 0x85EBCA77C2B2AE63;
|
||||||
|
static const int prime64_5 = 0x27D4EB2F165667C5;
|
||||||
|
|
||||||
|
int _acc1 = 0;
|
||||||
|
int _acc2 = 0;
|
||||||
|
int _acc3 = 0;
|
||||||
|
int _acc4 = 0;
|
||||||
|
|
||||||
|
late final Uint64List qbuffer = Uint64List.view(buffer.buffer);
|
||||||
|
|
||||||
|
XXHash64Sink(this.seed) : super(32) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_acc1 = seed + prime64_1 + prime64_2;
|
||||||
|
_acc2 = seed + prime64_2;
|
||||||
|
_acc3 = seed + 0;
|
||||||
|
_acc4 = seed - prime64_1;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
messageLength += end - start;
|
||||||
|
for (; start < end; start++, pos++) {
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
buffer[pos] = chunk[start];
|
||||||
|
}
|
||||||
|
if (pos == blockLength) {
|
||||||
|
$update();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _rotl(int x, int n) => (x << n) | (x >>> (64 - n));
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _accumulate(int x, int y) =>
|
||||||
|
_rotl((x + y * prime64_2), 31) * prime64_1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $update([List<int>? block, int offset = 0, bool last = false]) {
|
||||||
|
_acc1 = _accumulate(_acc1, qbuffer[0]);
|
||||||
|
_acc2 = _accumulate(_acc2, qbuffer[1]);
|
||||||
|
_acc3 = _accumulate(_acc3, qbuffer[2]);
|
||||||
|
_acc4 = _accumulate(_acc4, qbuffer[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
static int _merge(int h, int a) =>
|
||||||
|
(h ^ _accumulate(0, a)) * prime64_1 + prime64_4;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List $finalize() {
|
||||||
|
int i, t;
|
||||||
|
int hash;
|
||||||
|
|
||||||
|
if (messageLength < 32) {
|
||||||
|
hash = seed + prime64_5;
|
||||||
|
} else {
|
||||||
|
// accumulate
|
||||||
|
hash = _rotl(_acc1, 1);
|
||||||
|
hash += _rotl(_acc2, 7);
|
||||||
|
hash += _rotl(_acc3, 12);
|
||||||
|
hash += _rotl(_acc4, 18);
|
||||||
|
|
||||||
|
// merge round
|
||||||
|
hash = _merge(hash, _acc1);
|
||||||
|
hash = _merge(hash, _acc2);
|
||||||
|
hash = _merge(hash, _acc3);
|
||||||
|
hash = _merge(hash, _acc4);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash += messageLength;
|
||||||
|
|
||||||
|
// process the remaining data
|
||||||
|
for (i = t = 0; t + 8 <= pos; ++i, t += 8) {
|
||||||
|
hash ^= _accumulate(0, qbuffer[i]);
|
||||||
|
hash = _rotl(hash, 27);
|
||||||
|
hash *= prime64_1;
|
||||||
|
hash += prime64_4;
|
||||||
|
}
|
||||||
|
for (i <<= 1; t + 4 <= pos; ++i, t += 4) {
|
||||||
|
hash ^= sbuffer[i] * prime64_1;
|
||||||
|
hash = _rotl(hash, 23);
|
||||||
|
hash *= prime64_2;
|
||||||
|
hash += prime64_3;
|
||||||
|
}
|
||||||
|
for (; t < pos; t++) {
|
||||||
|
hash ^= buffer[t] * prime64_5;
|
||||||
|
hash = _rotl(hash, 11);
|
||||||
|
hash *= prime64_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// avalanche
|
||||||
|
hash ^= hash >>> 33;
|
||||||
|
hash *= prime64_2;
|
||||||
|
hash ^= hash >>> 29;
|
||||||
|
hash *= prime64_3;
|
||||||
|
hash ^= hash >>> 32;
|
||||||
|
|
||||||
|
return Uint8List.fromList([
|
||||||
|
hash >>> 56,
|
||||||
|
hash >>> 48,
|
||||||
|
hash >>> 40,
|
||||||
|
hash >>> 32,
|
||||||
|
hash >>> 24,
|
||||||
|
hash >>> 16,
|
||||||
|
hash >>> 8,
|
||||||
|
hash,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
110
hashlib/lib/src/argon2.dart
Normal file
110
hashlib/lib/src/argon2.dart
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
import 'package:hashlib/src/algorithms/argon2/argon2.dart';
|
||||||
|
|
||||||
|
export 'package:hashlib/src/algorithms/argon2/argon2.dart';
|
||||||
|
|
||||||
|
const _defaultSecurity = Argon2Security.good;
|
||||||
|
|
||||||
|
/// Verifies if the original [password] was derived from the [encoded]
|
||||||
|
/// Argon2 hash.
|
||||||
|
///
|
||||||
|
/// The encoded hash may look like this:
|
||||||
|
/// `$argon2i$v=19$m=16,t=2,p=1$c29tZSBzYWx0$u1eU6mZFG4/OOoTdAtM5SQ`
|
||||||
|
bool argon2Verify(String encoded, List<int> password) {
|
||||||
|
var data = fromCrypt(encoded);
|
||||||
|
var hash = data.hashBytes();
|
||||||
|
if (hash == null) {
|
||||||
|
throw ArgumentError('No password hash in the encoded string');
|
||||||
|
}
|
||||||
|
var instance = Argon2.fromEncoded(data);
|
||||||
|
return instance.verify(hash, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode a password using default Argon2d algorithm
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [password] is the raw password to be encoded
|
||||||
|
/// - [salt] should be at least 8 bytes long. 16 bytes is recommended.
|
||||||
|
/// - [security] is the parameter choice for the algorithm.
|
||||||
|
/// - [hashLength] is the number of output bytes
|
||||||
|
/// - [key] is an optional key bytes to use
|
||||||
|
/// - [personalization] is optional additional data bytes
|
||||||
|
Argon2HashDigest argon2d(
|
||||||
|
List<int> password,
|
||||||
|
List<int> salt, {
|
||||||
|
int? hashLength,
|
||||||
|
List<int>? key,
|
||||||
|
List<int>? personalization,
|
||||||
|
Argon2Security security = _defaultSecurity,
|
||||||
|
}) =>
|
||||||
|
Argon2(
|
||||||
|
salt: salt,
|
||||||
|
type: Argon2Type.argon2d,
|
||||||
|
hashLength: hashLength,
|
||||||
|
iterations: security.t,
|
||||||
|
parallelism: security.p,
|
||||||
|
memorySizeKB: security.m,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
).convert(password);
|
||||||
|
|
||||||
|
/// Encode a password using default Argon2i algorithm
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [password] is the raw password to be encoded
|
||||||
|
/// - [salt] should be at least 8 bytes long. 16 bytes is recommended.
|
||||||
|
/// - [security] is the parameter choice for the algorithm.
|
||||||
|
/// - [hashLength] is the number of output bytes
|
||||||
|
/// - [key] is an optional key bytes to use
|
||||||
|
/// - [personalization] is optional additional data bytes
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Argon2HashDigest argon2i(
|
||||||
|
List<int> password,
|
||||||
|
List<int> salt, {
|
||||||
|
int? hashLength,
|
||||||
|
List<int>? key,
|
||||||
|
List<int>? personalization,
|
||||||
|
Argon2Security security = _defaultSecurity,
|
||||||
|
}) =>
|
||||||
|
Argon2(
|
||||||
|
salt: salt,
|
||||||
|
type: Argon2Type.argon2i,
|
||||||
|
hashLength: hashLength,
|
||||||
|
iterations: security.t,
|
||||||
|
parallelism: security.p,
|
||||||
|
memorySizeKB: security.m,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
).convert(password);
|
||||||
|
|
||||||
|
/// Encode a password using default Argon2id algorithm
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [password] is the raw password to be encoded
|
||||||
|
/// - [salt] should be at least 8 bytes long. 16 bytes is recommended.
|
||||||
|
/// - [security] is the parameter choice for the algorithm.
|
||||||
|
/// - [hashLength] is the number of output bytes
|
||||||
|
/// - [key] is an optional key bytes to use
|
||||||
|
/// - [personalization] is optional additional data bytes
|
||||||
|
Argon2HashDigest argon2id(
|
||||||
|
List<int> password,
|
||||||
|
List<int> salt, {
|
||||||
|
int? hashLength,
|
||||||
|
List<int>? key,
|
||||||
|
List<int>? personalization,
|
||||||
|
Argon2Security security = _defaultSecurity,
|
||||||
|
}) =>
|
||||||
|
Argon2(
|
||||||
|
salt: salt,
|
||||||
|
type: Argon2Type.argon2id,
|
||||||
|
hashLength: hashLength,
|
||||||
|
iterations: security.t,
|
||||||
|
parallelism: security.p,
|
||||||
|
memorySizeKB: security.m,
|
||||||
|
key: key,
|
||||||
|
personalization: personalization,
|
||||||
|
).convert(password);
|
||||||
88
hashlib/lib/src/bcrypt.dart
Normal file
88
hashlib/lib/src/bcrypt.dart
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
import 'package:hashlib/src/algorithms/bcrypt/bcrypt.dart';
|
||||||
|
import 'package:hashlib/src/algorithms/bcrypt/common.dart';
|
||||||
|
import 'package:hashlib/src/algorithms/bcrypt/security.dart';
|
||||||
|
|
||||||
|
export 'algorithms/bcrypt/bcrypt.dart' show Bcrypt;
|
||||||
|
export 'algorithms/bcrypt/common.dart'
|
||||||
|
show BcryptContext, BcryptHashDigest, BcryptVersion;
|
||||||
|
export 'algorithms/bcrypt/security.dart' show BcryptSecurity;
|
||||||
|
|
||||||
|
/// Generate a secure password using the [Bcrypt] algorithm.
|
||||||
|
///
|
||||||
|
/// [Bcrypt][wiki] is a password hashing algorithm based on the Blowfish cipher.
|
||||||
|
/// It uses a unique salt and a configurable cost factor to prevent rainbow
|
||||||
|
/// table attacks and remain secure as hardware improves. Bcrypt's iterative
|
||||||
|
/// hashing and expensive key setup phase ensure resistance to brute-force
|
||||||
|
/// attacks.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [password] : The password to hash. The algorithm uses first 72 bytes, and
|
||||||
|
/// the rest are ignored.
|
||||||
|
/// - [salt] : An randomly generated 16-byte string.
|
||||||
|
/// - [nb] : Number of rounds in terms of power of 2. 0 < [nb] < 31
|
||||||
|
/// - [version] : The [BcryptVersion] to use. Default [BcryptVersion.$2b]
|
||||||
|
/// - [security] : The default parameter source for [nb] if not provided
|
||||||
|
/// (default is [BcryptSecurity.good]).
|
||||||
|
///
|
||||||
|
/// [wiki]: https://en.wikipedia.org/wiki/Bcrypt
|
||||||
|
BcryptHashDigest bcryptDigest(
|
||||||
|
List<int> password, {
|
||||||
|
List<int>? salt,
|
||||||
|
BcryptVersion version = BcryptVersion.$2b,
|
||||||
|
BcryptSecurity security = BcryptSecurity.good,
|
||||||
|
int? nb,
|
||||||
|
}) =>
|
||||||
|
Bcrypt(
|
||||||
|
version: version,
|
||||||
|
salt: salt,
|
||||||
|
cost: nb ?? security.nb,
|
||||||
|
).convert(password);
|
||||||
|
|
||||||
|
/// Generate the encoded salt to be used by the [Bcrypt] algorithm.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [nb] : Number of rounds in terms of power of 2. 0 < [nb] < 31
|
||||||
|
/// - [version] : The [BcryptVersion] to use. Default [BcryptVersion.$2b]
|
||||||
|
/// - [security] : The default parameter source for [nb] if not provided
|
||||||
|
/// (default is [BcryptSecurity.good]).
|
||||||
|
String bcryptSalt({
|
||||||
|
int? nb,
|
||||||
|
BcryptVersion version = BcryptVersion.$2b,
|
||||||
|
BcryptSecurity security = BcryptSecurity.good,
|
||||||
|
}) =>
|
||||||
|
BcryptContext(
|
||||||
|
version: version,
|
||||||
|
cost: nb ?? security.nb,
|
||||||
|
).toEncoded();
|
||||||
|
|
||||||
|
/// Generates a secure password using the [Bcrypt] algorithm, and returns a
|
||||||
|
/// 60-byte encoded string containing the version, cost, salt, and password.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [password] : The password to hash. The algorithm uses first 72 bytes, and
|
||||||
|
/// the rest are ignored.
|
||||||
|
/// - [salt] : Encoded salt, e.g.: `$2b$04$SQe9knOzepOVKoYXo9xTte`
|
||||||
|
/// If not provided, [bcryptSalt] is used to get a random one.
|
||||||
|
///
|
||||||
|
/// **Note**: Complete encoded string is also accepted, e.g.:
|
||||||
|
/// `$2b$04$SQe9knOzepOVKoYXo9xTteNYr6MBwVz4tpriJVe3PNgYufGIsgKcW`
|
||||||
|
String bcrypt(List<int> password, [String? salt]) =>
|
||||||
|
Bcrypt.fromEncoded(fromCrypt(salt ?? bcryptSalt()))
|
||||||
|
.convert(password)
|
||||||
|
.encoded();
|
||||||
|
|
||||||
|
/// Verifies if the [plain] password was derived from the [encoded] hash.
|
||||||
|
///
|
||||||
|
/// The encoded hash may look like this:
|
||||||
|
/// `$2b$04$SQe9knOzepOVKoYXo9xTteNYr6MBwVz4tpriJVe3PNgYufGIsgKcW`
|
||||||
|
bool bcryptVerify(String encoded, List<int> plain) {
|
||||||
|
var data = fromCrypt(encoded);
|
||||||
|
var hash = data.hash!.substring(22);
|
||||||
|
var hashBytes = fromBase64(hash, codec: Base64Codec.bcrypt);
|
||||||
|
var instance = Bcrypt.fromEncoded(data);
|
||||||
|
return instance.verify(hashBytes, plain);
|
||||||
|
}
|
||||||
126
hashlib/lib/src/blake2b.dart
Normal file
126
hashlib/lib/src/blake2b.dart
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'package:hashlib/src/algorithms/blake2/blake2b.dart';
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
export 'algorithms/blake2/blake2b.dart' show Blake2bHash;
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2b-160.
|
||||||
|
///
|
||||||
|
/// Use [Blake2b] for keyed hash generation.
|
||||||
|
const Blake2b blake2b160 = Blake2b._(160 >>> 3);
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2b-256.
|
||||||
|
///
|
||||||
|
/// Use [Blake2b] for keyed hash generation.
|
||||||
|
const Blake2b blake2b256 = Blake2b._(256 >>> 3);
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2b-384.
|
||||||
|
///
|
||||||
|
/// Use [Blake2b] for keyed hash generation.
|
||||||
|
const Blake2b blake2b384 = Blake2b._(384 >>> 3);
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2b-512.
|
||||||
|
///
|
||||||
|
/// Use [Blake2b] for keyed hash generation.
|
||||||
|
const Blake2b blake2b512 = Blake2b._(512 >>> 3);
|
||||||
|
|
||||||
|
/// Blake2b is a highly secure cryptographic hash function optimized for 64-bit
|
||||||
|
/// platforms. It generates hash values of data ranging from 1 to 64 bytes in
|
||||||
|
/// size. It doesn't require a separate keying mechanism and can be used in
|
||||||
|
/// various applications, serving as a more efficient alternative to other hash
|
||||||
|
/// algorithms like SHA and HMAC-SHA.
|
||||||
|
///
|
||||||
|
/// This implementation is based on the [RFC-7693][rfc]
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc7693.html
|
||||||
|
class Blake2b extends BlockHashBase<Blake2bHash> with MACHashBase<Blake2bHash> {
|
||||||
|
final List<int>? _key;
|
||||||
|
final List<int>? _salt;
|
||||||
|
final List<int>? _aad;
|
||||||
|
|
||||||
|
/// The number of bytes in the output.
|
||||||
|
final int digestSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
const Blake2b._(
|
||||||
|
this.digestSize, [
|
||||||
|
this._key,
|
||||||
|
this._salt,
|
||||||
|
this._aad,
|
||||||
|
String? _name,
|
||||||
|
]) : name = _name ?? 'BLAKE2b-${digestSize << 3}';
|
||||||
|
|
||||||
|
/// Creates an instance to generate hash using BLAKE-2b algorithm.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [digestSize] The number of bytes in the output.
|
||||||
|
/// - [salt] An optional nonce. Must be exactly 16 bytes long.
|
||||||
|
/// - [aad] Second optional nonce. Must be exactly 16 bytes long.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// - [mac] or [Blake2bMAC] for generating MAC with this algorithm.
|
||||||
|
factory Blake2b(
|
||||||
|
int digestSize, {
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? aad,
|
||||||
|
}) =>
|
||||||
|
Blake2b._(digestSize, null, salt, aad);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Blake2bHash createSink() => Blake2bHash(
|
||||||
|
digestSize,
|
||||||
|
key: _key,
|
||||||
|
salt: _salt,
|
||||||
|
aad: _aad,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Get a builder to generate MAC using this algorithm.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// final key = 'secret key'.codeUnits;
|
||||||
|
/// final message = 'plain message'.codeUnits;
|
||||||
|
/// final mac = blake2s256.mac.by(key).convert(message);
|
||||||
|
/// ```
|
||||||
|
Blake2bMAC get mac => Blake2bMAC(digestSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A MAC generator based on Blake2b algorithm.
|
||||||
|
class Blake2bMAC extends MACHash<Blake2bHash> {
|
||||||
|
/// The number of bytes in the output.
|
||||||
|
final int digestSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Creates an instance to generate MAC using BLAKE-2b algorithm.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [digestSize] The number of bytes in the output.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// - [Blake2b] for generating hash only.
|
||||||
|
const Blake2bMAC(
|
||||||
|
this.digestSize,
|
||||||
|
) : name = 'BLAKE2b-${digestSize << 3}/MAC';
|
||||||
|
|
||||||
|
/// Get an [MACHashBase] instance initialized by a [key].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [key] An optional key for MAC generation. Should not exceed 64 bytes.
|
||||||
|
/// - [salt] An optional nonce. Must be exactly 16 bytes long.
|
||||||
|
/// - [aad] Second optional nonce. Must be exactly 16 bytes long.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
MACHashBase<Blake2bHash> by(
|
||||||
|
List<int> key, {
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? aad,
|
||||||
|
}) =>
|
||||||
|
Blake2b._(digestSize, key, salt, aad, name);
|
||||||
|
}
|
||||||
125
hashlib/lib/src/blake2s.dart
Normal file
125
hashlib/lib/src/blake2s.dart
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'package:hashlib/src/algorithms/blake2/blake2s.dart';
|
||||||
|
import 'package:hashlib/src/core/block_hash.dart';
|
||||||
|
import 'package:hashlib/src/core/mac_base.dart';
|
||||||
|
|
||||||
|
export 'algorithms/blake2/blake2s.dart' show Blake2sHash;
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2s-128.
|
||||||
|
///
|
||||||
|
/// Use [Blake2s] for keyed hash generation.
|
||||||
|
const Blake2s blake2s128 = Blake2s._(128 >>> 3);
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2s-160.
|
||||||
|
///
|
||||||
|
/// Use [Blake2s] for keyed hash generation.
|
||||||
|
const Blake2s blake2s160 = Blake2s._(160 >>> 3);
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2s-224.
|
||||||
|
///
|
||||||
|
/// Use [Blake2s] for keyed hash generation.
|
||||||
|
const Blake2s blake2s224 = Blake2s._(224 >>> 3);
|
||||||
|
|
||||||
|
/// For generating un-keyed message digest with BLAKE2s-256.
|
||||||
|
///
|
||||||
|
/// Use [Blake2s] for keyed hash generation.
|
||||||
|
const Blake2s blake2s256 = Blake2s._(256 >>> 3);
|
||||||
|
|
||||||
|
/// Blake2s is a cryptographic hash function optimized for 8-bit to 32-bit
|
||||||
|
/// platforms. It generates hash values of data ranging from 1 to 32 bytes in
|
||||||
|
/// size. Blake2s is highly secure and can be used in various applications as a
|
||||||
|
/// fast and secure replacement for legacy algorithms like MD5 and HMAC-MD5.
|
||||||
|
///
|
||||||
|
/// This implementation is based on the [RFC-7693][rfc]
|
||||||
|
///
|
||||||
|
/// [rfc]: https://www.ietf.org/rfc/rfc7693.html
|
||||||
|
class Blake2s extends BlockHashBase<Blake2sHash> with MACHashBase<Blake2sHash> {
|
||||||
|
final List<int>? _key;
|
||||||
|
final List<int>? _salt;
|
||||||
|
final List<int>? _aad;
|
||||||
|
|
||||||
|
/// The number of bytes in the output.
|
||||||
|
final int digestSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
const Blake2s._(
|
||||||
|
this.digestSize, [
|
||||||
|
this._key,
|
||||||
|
this._salt,
|
||||||
|
this._aad,
|
||||||
|
String? _name,
|
||||||
|
]) : name = _name ?? 'BLAKE2s-${digestSize << 3}';
|
||||||
|
|
||||||
|
/// Creates an instance to generate hash using BLAKE-2s algorithm.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [digestSize] The number of bytes in the output.
|
||||||
|
/// - [salt] An optional nonce. Must be exactly 8 bytes long.
|
||||||
|
/// - [aad] Second optional nonce. Must be exactly 8 bytes long.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// - [mac] or [Blake2sMAC] for generating MAC with this algorithm.
|
||||||
|
factory Blake2s(
|
||||||
|
int digestSize, {
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? aad,
|
||||||
|
}) =>
|
||||||
|
Blake2s._(digestSize, null, salt, aad);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Blake2sHash createSink() => Blake2sHash(
|
||||||
|
digestSize,
|
||||||
|
key: _key,
|
||||||
|
salt: _salt,
|
||||||
|
aad: _aad,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Get a builder to generate MAC using this algorithm.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// final key = 'secret key'.codeUnits;
|
||||||
|
/// final message = 'plain message'.codeUnits;
|
||||||
|
/// final mac = blake2s256.mac.by(key).convert(message);
|
||||||
|
/// ```
|
||||||
|
Blake2sMAC get mac => Blake2sMAC(digestSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A MAC generator based on Blake2s algorithm.
|
||||||
|
class Blake2sMAC extends MACHash<Blake2sHash> {
|
||||||
|
/// The number of bytes in the output.
|
||||||
|
final int digestSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Creates an instance to generate MAC using BLAKE-2s algorithm.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [digestSize] The number of bytes in the output.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
/// - [Blake2s] for generating hash only.
|
||||||
|
const Blake2sMAC(
|
||||||
|
this.digestSize,
|
||||||
|
) : name = 'BLAKE2s-${digestSize << 3}/MAC';
|
||||||
|
|
||||||
|
/// Get an [MACHashBase] instance initialized by a [key].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [key] The key for MAC generation. Should not exceed 32 bytes.
|
||||||
|
/// - [salt] An optional nonce. Must be exactly 8 bytes long.
|
||||||
|
/// - [aad] Second optional nonce. Must be exactly 8 bytes long.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
MACHashBase<Blake2sHash> by(
|
||||||
|
List<int> key, {
|
||||||
|
List<int>? salt,
|
||||||
|
List<int>? aad,
|
||||||
|
}) =>
|
||||||
|
Blake2s._(digestSize, key, salt, aad, name);
|
||||||
|
}
|
||||||
82
hashlib/lib/src/core/block_hash.dart
Normal file
82
hashlib/lib/src/core/block_hash.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
/// Base class used by block hash algorithms
|
||||||
|
abstract class BlockHashBase<T extends BlockHashSink> extends HashBase<T> {
|
||||||
|
const BlockHashBase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Base class used by block hash algorithm sink implementations
|
||||||
|
abstract class BlockHashSink extends HashDigestSink {
|
||||||
|
/// The current position of data in the [buffer]
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
/// The message length in bytes
|
||||||
|
int messageLength = 0;
|
||||||
|
|
||||||
|
/// The internal block length of the algorithm in bytes
|
||||||
|
final int blockLength;
|
||||||
|
|
||||||
|
/// The main buffer
|
||||||
|
final Uint8List buffer;
|
||||||
|
|
||||||
|
/// The [buffer] as Uint32List
|
||||||
|
late final Uint32List sbuffer = Uint32List.view(buffer.buffer);
|
||||||
|
|
||||||
|
/// The [buffer] as ByteData
|
||||||
|
late final ByteData bdata = buffer.buffer.asByteData();
|
||||||
|
|
||||||
|
/// Get the message length in bits
|
||||||
|
int get messageLengthInBits => messageLength << 3;
|
||||||
|
|
||||||
|
/// Initialize a new sink for the block hash
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [blockLength] is the length of each block in each [$update] call.
|
||||||
|
/// - [bufferLength] is the buffer length where blocks are stored temporarily
|
||||||
|
BlockHashSink(this.blockLength, {int? bufferLength})
|
||||||
|
: assert(blockLength > 0 && (bufferLength ?? 0) >= 0),
|
||||||
|
buffer = Uint8List(bufferLength ?? blockLength);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
pos = 0;
|
||||||
|
messageLength = 0;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void $process(List<int> chunk, int start, int end) {
|
||||||
|
int t = start;
|
||||||
|
if (pos > 0) {
|
||||||
|
for (; t < end && pos < blockLength; pos++, t++) {
|
||||||
|
buffer[pos] = chunk[t];
|
||||||
|
}
|
||||||
|
messageLength += t - start;
|
||||||
|
if (pos < blockLength) return;
|
||||||
|
|
||||||
|
$update(buffer);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((end - t) >= blockLength) {
|
||||||
|
messageLength += blockLength;
|
||||||
|
$update(chunk, t);
|
||||||
|
t += blockLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
messageLength += end - t;
|
||||||
|
for (; t < end; pos++, t++) {
|
||||||
|
buffer[pos] = chunk[t];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal method to update the message-digest with a single [block].
|
||||||
|
///
|
||||||
|
/// The method starts reading the block from [offset] index
|
||||||
|
void $update(List<int> block, [int offset = 0, bool last = false]);
|
||||||
|
}
|
||||||
170
hashlib/lib/src/core/hash_base.dart
Normal file
170
hashlib/lib/src/core/hash_base.dart
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert' show Encoding;
|
||||||
|
import 'dart:typed_data' show Uint8List;
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_digest.dart';
|
||||||
|
|
||||||
|
export 'hash_base_stub.dart' if (dart.library.io) 'hash_base_io.dart';
|
||||||
|
|
||||||
|
/// This sink allows adding arbitrary length byte arrays
|
||||||
|
/// and produces a [HashDigest] on [close].
|
||||||
|
abstract class HashDigestSink implements Sink<List<int>> {
|
||||||
|
HashDigestSink();
|
||||||
|
|
||||||
|
/// The flag tracking if the [digest] is called once.
|
||||||
|
bool _closed = false;
|
||||||
|
|
||||||
|
/// The message digest (available after the [digest] call)
|
||||||
|
late HashDigest _digest;
|
||||||
|
|
||||||
|
/// Returns true if the sink is closed, false otherwise
|
||||||
|
bool get closed => _closed;
|
||||||
|
|
||||||
|
/// The length of generated hash in bytes
|
||||||
|
int get hashLength;
|
||||||
|
|
||||||
|
/// Resets the current state to start from fresh state
|
||||||
|
void reset() {
|
||||||
|
_closed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds [data] to the message-digest.
|
||||||
|
///
|
||||||
|
/// Throws [StateError], if it is called after closing the digest.
|
||||||
|
@override
|
||||||
|
void add(List<int> data, [int start = 0, int? end]) {
|
||||||
|
if (_closed) {
|
||||||
|
throw StateError('The message-digest is already closed');
|
||||||
|
}
|
||||||
|
$process(data, start, end ?? data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes a chunk of input data
|
||||||
|
void $process(List<int> chunk, int start, int end);
|
||||||
|
|
||||||
|
/// Finalizes the message-digest and returns a [HashDigest]
|
||||||
|
HashDigest digest() {
|
||||||
|
if (_closed) return _digest;
|
||||||
|
_digest = HashDigest($finalize());
|
||||||
|
_closed = true;
|
||||||
|
return _digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finalizes the message digest with the remaining message block,
|
||||||
|
/// and returns the output as byte array.
|
||||||
|
Uint8List $finalize();
|
||||||
|
|
||||||
|
/// Finalizes the message-digest. It calls [digest] method internally.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
void close() => digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The base class used by the hash algorithm implementations. It implements
|
||||||
|
/// the [StreamTransformer] and exposes few convenient methods to handle any
|
||||||
|
/// types of data source.
|
||||||
|
abstract class HashBase<T extends HashDigestSink>
|
||||||
|
implements StreamTransformer<List<int>, HashDigest> {
|
||||||
|
const HashBase();
|
||||||
|
|
||||||
|
/// The name of this algorithm
|
||||||
|
String get name;
|
||||||
|
|
||||||
|
/// Create a [HashDigestSink] for generating message-digests
|
||||||
|
T createSink();
|
||||||
|
|
||||||
|
/// Process the byte array [input] and returns a [HashDigest].
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
HashDigest convert(List<int> input) {
|
||||||
|
var sink = createSink();
|
||||||
|
sink.add(input);
|
||||||
|
return sink.digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process the byte array [input] and returns a hash in hexadecimal.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
String hex(List<int> input) => convert(input).hex();
|
||||||
|
|
||||||
|
/// Process the [input] string and returns a [HashDigest].
|
||||||
|
///
|
||||||
|
/// If [encoding] is not specified, the [String.codeUnits] are used.
|
||||||
|
HashDigest string(String input, [Encoding? encoding]) {
|
||||||
|
var sink = createSink();
|
||||||
|
if (encoding != null) {
|
||||||
|
sink.add(encoding.encode(input));
|
||||||
|
} else {
|
||||||
|
sink.add(input.codeUnits);
|
||||||
|
}
|
||||||
|
return sink.digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms the byte array input stream to generate a new stream
|
||||||
|
/// which contains a single [HashDigest]
|
||||||
|
///
|
||||||
|
/// The expected behavior of this method is described below:
|
||||||
|
///
|
||||||
|
/// - When the returned stream has a subscriber (calling [Stream.listen]),
|
||||||
|
/// the message-digest generation begins from the input [stream].
|
||||||
|
/// - If the returned stream is paused, the processing of the input [stream]
|
||||||
|
/// is also paused, and on resume, it continue processing from where it was
|
||||||
|
/// left off.
|
||||||
|
/// - If the returned stream is cancelled, the subscription to the input
|
||||||
|
/// [stream] is also cancelled.
|
||||||
|
/// - When the input [stream] is closed, the returned stream also gets closed
|
||||||
|
/// with a [HashDigest] data. The returned stream may produce only one
|
||||||
|
/// such data event in its life-time.
|
||||||
|
/// - On error reading the input [stream], or while processing the message
|
||||||
|
/// digest, the subscription to input [stream] cancels immediately and the
|
||||||
|
/// returned stream closes with an error event.
|
||||||
|
@override
|
||||||
|
Stream<HashDigest> bind(Stream<List<int>> stream) async* {
|
||||||
|
var sink = createSink();
|
||||||
|
await for (var x in stream) {
|
||||||
|
sink.add(x);
|
||||||
|
}
|
||||||
|
yield sink.digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the entire [stream] of byte array and generates a [HashDigest].
|
||||||
|
Future<HashDigest> byteStream(
|
||||||
|
Stream<int> stream, [
|
||||||
|
int bufferSize = 1024,
|
||||||
|
]) async {
|
||||||
|
var sink = createSink();
|
||||||
|
var buffer = Uint8List(bufferSize);
|
||||||
|
int p = 0;
|
||||||
|
await for (var x in stream) {
|
||||||
|
buffer[p++] = x;
|
||||||
|
if (p == bufferSize) {
|
||||||
|
sink.add(buffer);
|
||||||
|
p = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p > 0) {
|
||||||
|
sink.add(buffer, 0, p);
|
||||||
|
}
|
||||||
|
return sink.digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the entire [stream] of string and generates a [HashDigest].
|
||||||
|
///
|
||||||
|
/// If [encoding] is not specified, the [String.codeUnits] are used.
|
||||||
|
Future<HashDigest> stringStraem(
|
||||||
|
Stream<String> stream, [
|
||||||
|
Encoding? encoding,
|
||||||
|
]) {
|
||||||
|
if (encoding == null) {
|
||||||
|
return bind(stream.map((s) => s.codeUnits)).first;
|
||||||
|
} else {
|
||||||
|
return bind(stream.transform(encoding.encoder)).first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
StreamTransformer<RS, RT> cast<RS, RT>() {
|
||||||
|
throw UnsupportedError('The transformer do not support casting');
|
||||||
|
}
|
||||||
|
}
|
||||||
55
hashlib/lib/src/core/hash_base_io.dart
Normal file
55
hashlib/lib/src/core/hash_base_io.dart
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'hash_base.dart';
|
||||||
|
import 'hash_digest.dart';
|
||||||
|
|
||||||
|
/// Extends the base hash algorithm class with file IO support
|
||||||
|
extension HashBaseFileSupport on HashBase {
|
||||||
|
/// Converts the [input] file and returns a [HashDigest] asynchronously.
|
||||||
|
///
|
||||||
|
/// If [start] is present, the file will be read from byte-offset [start].
|
||||||
|
/// Otherwise from the beginning (index 0).
|
||||||
|
///
|
||||||
|
/// If [end] is present, only bytes up to byte-index [end] will be read.
|
||||||
|
/// Otherwise, until end of file.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
Future<HashDigest> file(File input, [int start = 0, int? end]) {
|
||||||
|
return bind(input.openRead(start, end)).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the [input] file and returns a [HashDigest] synchronously.
|
||||||
|
///
|
||||||
|
/// If [start] is present, the file will be read from byte-offset [start].
|
||||||
|
/// Otherwise from the beginning (index 0).
|
||||||
|
///
|
||||||
|
/// If [end] is present, only bytes up to byte-index [end] will be read.
|
||||||
|
/// Otherwise, until end of file.
|
||||||
|
///
|
||||||
|
/// If [bufferSize] is present, the file will be read in chunks of this size.
|
||||||
|
/// By default the [bufferSize] is `2048`.
|
||||||
|
HashDigest fileSync(
|
||||||
|
File input, {
|
||||||
|
int start = 0,
|
||||||
|
int? end,
|
||||||
|
int bufferSize = 2048,
|
||||||
|
}) {
|
||||||
|
var raf = input.openSync();
|
||||||
|
try {
|
||||||
|
var sink = createSink();
|
||||||
|
var buffer = Uint8List(bufferSize);
|
||||||
|
int length = end ?? raf.lengthSync();
|
||||||
|
for (int i = start, l; i < length; i += l) {
|
||||||
|
l = raf.readIntoSync(buffer);
|
||||||
|
sink.add(buffer, 0, l);
|
||||||
|
}
|
||||||
|
return sink.digest();
|
||||||
|
} finally {
|
||||||
|
raf.closeSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
hashlib/lib/src/core/hash_base_stub.dart
Normal file
41
hashlib/lib/src/core/hash_base_stub.dart
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright (c) 2024, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'hash_base.dart';
|
||||||
|
import 'hash_digest.dart';
|
||||||
|
|
||||||
|
/// Stub for machines that does not have file IO support.
|
||||||
|
extension HashBaseFileSupport on HashBase {
|
||||||
|
/// Converts the [input] file and returns a [HashDigest] asynchronously.
|
||||||
|
///
|
||||||
|
/// If [start] is present, the file will be read from byte-offset [start].
|
||||||
|
/// Otherwise from the beginning (index 0).
|
||||||
|
///
|
||||||
|
/// If [end] is present, only bytes up to byte-index [end] will be read.
|
||||||
|
/// Otherwise, until end of file.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
Future<HashDigest> file(dynamic input, [int start = 0, int? end]) {
|
||||||
|
throw UnsupportedError('Unavailable for this platform');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the [input] file and returns a [HashDigest] synchronously.
|
||||||
|
///
|
||||||
|
/// If [start] is present, the file will be read from byte-offset [start].
|
||||||
|
/// Otherwise from the beginning (index 0).
|
||||||
|
///
|
||||||
|
/// If [end] is present, only bytes up to byte-index [end] will be read.
|
||||||
|
/// Otherwise, until end of file.
|
||||||
|
///
|
||||||
|
/// If [bufferSize] is present, the file will be read in chunks of this size.
|
||||||
|
/// By default the [bufferSize] is `2048`.
|
||||||
|
HashDigest fileSync(
|
||||||
|
dynamic input, {
|
||||||
|
int start = 0,
|
||||||
|
int? end,
|
||||||
|
int bufferSize = 2048,
|
||||||
|
}) {
|
||||||
|
throw UnsupportedError('Unavailable for this platform');
|
||||||
|
}
|
||||||
|
}
|
||||||
146
hashlib/lib/src/core/hash_digest.dart
Normal file
146
hashlib/lib/src/core/hash_digest.dart
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:convert' as cvt;
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:hashlib/codecs.dart';
|
||||||
|
|
||||||
|
/// This holds generated hash digest and provides utilities to extract it in
|
||||||
|
/// multiple formats.
|
||||||
|
class HashDigest extends Object {
|
||||||
|
final Uint8List bytes;
|
||||||
|
|
||||||
|
const HashDigest(this.bytes);
|
||||||
|
|
||||||
|
/// Returns the length of this digest in bytes.
|
||||||
|
int get length => bytes.length;
|
||||||
|
|
||||||
|
/// Returns the byte buffer associated with this digest.
|
||||||
|
ByteBuffer get buffer => bytes.buffer;
|
||||||
|
|
||||||
|
/// The message digest as a string of hexadecimal digits.
|
||||||
|
@override
|
||||||
|
String toString() => hex();
|
||||||
|
|
||||||
|
/// The message digest as a binary string.
|
||||||
|
String binary() => toBinary(bytes);
|
||||||
|
|
||||||
|
/// The message digest as a octal string.
|
||||||
|
String octal() => toOctal(bytes);
|
||||||
|
|
||||||
|
/// The message digest as a hexadecimal string.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - If [upper] is true, the string will be in uppercase alphabets.
|
||||||
|
String hex([bool upper = false]) => toHex(bytes, upper: upper);
|
||||||
|
|
||||||
|
/// The message digest as a Base-32 string.
|
||||||
|
///
|
||||||
|
/// If [upper] is true, the output will have uppercase alphabets.
|
||||||
|
/// If [padding] is true, the output will have `=` padding at the end.
|
||||||
|
String base32({bool upper = true, bool padding = true}) =>
|
||||||
|
toBase32(bytes, lower: !upper, padding: padding);
|
||||||
|
|
||||||
|
/// The message digest as a Base-64 string with no padding.
|
||||||
|
///
|
||||||
|
/// If [urlSafe] is true, the output will have URL-safe base64 alphabets.
|
||||||
|
/// If [padding] is true, the output will have `=` padding at the end.
|
||||||
|
String base64({bool urlSafe = false, bool padding = true}) =>
|
||||||
|
toBase64(bytes, padding: padding, url: urlSafe);
|
||||||
|
|
||||||
|
/// The message digest as a BigInt.
|
||||||
|
///
|
||||||
|
/// If [endian] is [Endian.little], it will treat the digest bytes as a little
|
||||||
|
/// endian number; Otherwise, if [endian] is [Endian.big], it will treat the
|
||||||
|
/// digest bytes as a big endian number.
|
||||||
|
BigInt bigInt({Endian endian = Endian.little}) =>
|
||||||
|
toBigInt(bytes, msbFirst: endian == Endian.big);
|
||||||
|
|
||||||
|
/// Gets unsiged integer of [bitLength]-bit from the message digest.
|
||||||
|
///
|
||||||
|
/// If [endian] is [Endian.little], it will treat the digest bytes as a little
|
||||||
|
/// endian number; Otherwise, if [endian] is [Endian.big], it will treat the
|
||||||
|
/// digest bytes as a big endian number.
|
||||||
|
int number([int bitLength = 64, Endian endian = Endian.big]) {
|
||||||
|
if (bitLength < 8 || bitLength > 64 || (bitLength & 7) > 0) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid bit length. '
|
||||||
|
'It must be a number between 8 to 64 and a multiple of 8.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
bitLength >>>= 3;
|
||||||
|
}
|
||||||
|
int result = 0;
|
||||||
|
int n = bytes.length;
|
||||||
|
if (endian == Endian.little) {
|
||||||
|
for (int i = (n > bitLength ? bitLength : n) - 1; i >= 0; i--) {
|
||||||
|
result <<= 8;
|
||||||
|
result |= bytes[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = n > bitLength ? n - bitLength : 0; i < n; i++) {
|
||||||
|
result <<= 8;
|
||||||
|
result |= bytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The message digest as a string of ASCII alphabets.
|
||||||
|
String ascii() => cvt.ascii.decode(bytes);
|
||||||
|
|
||||||
|
/// The message digest as a string of UTF-8 alphabets.
|
||||||
|
String utf8() => cvt.utf8.decode(bytes);
|
||||||
|
|
||||||
|
/// Returns the digest in the given [encoding]
|
||||||
|
String to(cvt.Encoding encoding) => encoding.decode(bytes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => bytes.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => other is HashDigest && bytes == other.bytes;
|
||||||
|
|
||||||
|
/// Checks if the message digest equals to [other].
|
||||||
|
///
|
||||||
|
/// Here, the [other] can be a one of the following:
|
||||||
|
/// - Another [HashDigest] object.
|
||||||
|
/// - An [Iterable] containing an array of bytes
|
||||||
|
/// - Any [ByteBuffer] or [TypedData] that will be converted to [Uint8List]
|
||||||
|
/// - A [String], which will be treated as a hexadecimal encoded byte array
|
||||||
|
///
|
||||||
|
/// This function will return True if all bytes in the [other] matches with
|
||||||
|
/// the [bytes] of this object. If the length does not match, or the type of
|
||||||
|
/// [other] is not supported, it returns False immediately.
|
||||||
|
bool isEqual(dynamic other) {
|
||||||
|
if (other is HashDigest) {
|
||||||
|
return isEqual(other.bytes);
|
||||||
|
} else if (other is ByteBuffer) {
|
||||||
|
return isEqual(Uint8List.view(buffer));
|
||||||
|
} else if (other is TypedData && other is! Uint8List) {
|
||||||
|
return isEqual(Uint8List.view(other.buffer));
|
||||||
|
} else if (other is String) {
|
||||||
|
return isEqual(fromHex(other));
|
||||||
|
} else if (other is List<int>) {
|
||||||
|
if (other.length != bytes.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < bytes.length; ++i) {
|
||||||
|
if (other[i] != bytes[i++]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (other is Iterable<int>) {
|
||||||
|
int i = 0;
|
||||||
|
for (int x in other) {
|
||||||
|
if (i >= bytes.length || x != bytes[i++]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
hashlib/lib/src/core/kdf_base.dart
Normal file
47
hashlib/lib/src/core/kdf_base.dart
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_digest.dart';
|
||||||
|
|
||||||
|
/// Base class for key generator algorithms
|
||||||
|
abstract class KeyDerivatorBase {
|
||||||
|
const KeyDerivatorBase();
|
||||||
|
|
||||||
|
/// The name of this algorithm
|
||||||
|
String get name;
|
||||||
|
|
||||||
|
/// The length of derived key in bytes
|
||||||
|
int get derivedKeyLength;
|
||||||
|
|
||||||
|
/// Generate a derived key from a [password]
|
||||||
|
HashDigest convert(List<int> password);
|
||||||
|
|
||||||
|
/// Returns the derived key in hexadecimal.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
String hex(List<int> password) => convert(password).hex();
|
||||||
|
|
||||||
|
/// Process the [password] string and returns derived key as [HashDigest].
|
||||||
|
///
|
||||||
|
/// If [encoding] is not specified, the [String.codeUnits] are used.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
HashDigest string(String password, [Encoding? encoding]) => convert(
|
||||||
|
encoding != null ? encoding.encode(password) : password.codeUnits,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Verify if the [derivedKey] was derived from the original [password]
|
||||||
|
/// using the current parameters.
|
||||||
|
bool verify(List<int> derivedKey, List<int> password) {
|
||||||
|
if (derivedKey.length != derivedKeyLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var other = convert(password).bytes;
|
||||||
|
for (int i = 0; i < other.length; ++i) {
|
||||||
|
if (derivedKey[i] != other[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
hashlib/lib/src/core/mac_base.dart
Normal file
52
hashlib/lib/src/core/mac_base.dart
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
import 'package:hashlib/src/core/hash_digest.dart';
|
||||||
|
|
||||||
|
/// Base class for the sink used by Message-Authentication-Code generators
|
||||||
|
abstract class MACSinkBase extends HashDigestSink {
|
||||||
|
/// The length of generated key in bytes
|
||||||
|
int get derivedKeyLength => hashLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This can be used as a mixin for MAC algorithm interfaces
|
||||||
|
abstract class MACHashBase<T extends HashDigestSink> implements HashBase<T> {
|
||||||
|
/// Signing the [message] using this MAC to generate a tag.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
HashDigest sign(List<int> message) => convert(message);
|
||||||
|
|
||||||
|
/// Verify if the [tag] is derived from the [message] using this MAC.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
bool verify(List<int> tag, List<int> message) =>
|
||||||
|
convert(message).isEqual(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MACHash<T extends HashDigestSink> {
|
||||||
|
const MACHash();
|
||||||
|
|
||||||
|
/// The name of this algorithm
|
||||||
|
String get name;
|
||||||
|
|
||||||
|
/// Get a [MACHashBase] instance initialized by a [key].
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
MACHashBase<T> by(List<int> key);
|
||||||
|
|
||||||
|
/// Get a [MACHashBase] instance initialized by a string [key].
|
||||||
|
///
|
||||||
|
/// If [encoding] is not specified, the [String.codeUnits] are used.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
MACHashBase<T> byString(String key, [Encoding? encoding]) =>
|
||||||
|
by(encoding != null ? encoding.encode(key) : key.codeUnits);
|
||||||
|
|
||||||
|
/// Signing the [message] using a [key] to generate a tag.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
HashDigest sign(List<int> key, List<int> message) => by(key).sign(message);
|
||||||
|
|
||||||
|
/// Verify if the [tag] is derived from the [message] using a [key].
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
bool verify(List<int> key, List<int> tag, List<int> message) =>
|
||||||
|
by(key).verify(tag, message);
|
||||||
|
}
|
||||||
30
hashlib/lib/src/core/otpauth.dart
Normal file
30
hashlib/lib/src/core/otpauth.dart
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
/// Represents an abstract class for implementing [ One-Time Password (OTP)
|
||||||
|
/// authentication ][rfc2289] methods in Dart.
|
||||||
|
///
|
||||||
|
/// This class provides a foundation for creating variable length OTP generation
|
||||||
|
/// algorithms. Subclasses must implement the [value] method to generate OTP
|
||||||
|
/// values based on a specific algorithm.
|
||||||
|
///
|
||||||
|
/// [rfc2289]: https://www.ietf.org/rfc/rfc2289.html
|
||||||
|
abstract class OTPAuth {
|
||||||
|
/// The number of digits in the generated OTP
|
||||||
|
final int digits;
|
||||||
|
final String? label;
|
||||||
|
final String? issuer;
|
||||||
|
|
||||||
|
const OTPAuth(
|
||||||
|
this.digits, {
|
||||||
|
this.label,
|
||||||
|
this.issuer,
|
||||||
|
}) : assert(digits >= 4);
|
||||||
|
|
||||||
|
/// Generates the next OTP value
|
||||||
|
int value();
|
||||||
|
|
||||||
|
/// Returns the next OTP value as string
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
String valueString() => value().toString().padLeft(digits, '0');
|
||||||
|
}
|
||||||
141
hashlib/lib/src/core/registry.dart
Normal file
141
hashlib/lib/src/core/registry.dart
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'package:hashlib/hashlib.dart';
|
||||||
|
|
||||||
|
const int _zero = 48;
|
||||||
|
const int _nine = 57;
|
||||||
|
const int _smallA = 97;
|
||||||
|
const int _smallZ = 122;
|
||||||
|
const int _bigA = 65;
|
||||||
|
const int _bigZ = 90;
|
||||||
|
|
||||||
|
final _hash = <String, HashBase>{};
|
||||||
|
final _blockHash = <String, BlockHashBase>{};
|
||||||
|
|
||||||
|
/// Transform [value] to uppercase and keeps only letters and digits.
|
||||||
|
@Deprecated('It will be removed in 2.0.0')
|
||||||
|
String normalizeName(String value) {
|
||||||
|
List<int> normal = [];
|
||||||
|
for (int c in value.codeUnits) {
|
||||||
|
if ((c >= _zero && c <= _nine) || (c >= _bigA && c <= _bigZ)) {
|
||||||
|
normal.add(c);
|
||||||
|
} else if (c >= _smallA && c <= _smallZ) {
|
||||||
|
normal.add(c - _smallA + _bigA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.fromCharCodes(normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
String _norm(String name) =>
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
normalizeName(name);
|
||||||
|
|
||||||
|
void _buildRegistry() {
|
||||||
|
if (_hash.isNotEmpty) return;
|
||||||
|
|
||||||
|
_hash.addAll({
|
||||||
|
_norm(adler32.name): adler32,
|
||||||
|
_norm(crc16.name): crc16,
|
||||||
|
_norm(crc32.name): crc32,
|
||||||
|
_norm(crc64.name): crc64,
|
||||||
|
});
|
||||||
|
|
||||||
|
_blockHash.addAll({
|
||||||
|
_norm(blake2b160.name): blake2b160,
|
||||||
|
_norm(blake2b256.name): blake2b256,
|
||||||
|
_norm(blake2b384.name): blake2b384,
|
||||||
|
_norm(blake2b512.name): blake2b512,
|
||||||
|
_norm('BLAKE2'): blake2b512,
|
||||||
|
_norm('BLAKE2b'): blake2b512,
|
||||||
|
_norm(blake2s128.name): blake2s128,
|
||||||
|
_norm(blake2s160.name): blake2s160,
|
||||||
|
_norm(blake2s224.name): blake2s224,
|
||||||
|
_norm(blake2s256.name): blake2s256,
|
||||||
|
_norm('BLAKE2s'): blake2s256,
|
||||||
|
_norm(keccak224.name): keccak224,
|
||||||
|
_norm(keccak256.name): keccak256,
|
||||||
|
_norm(keccak384.name): keccak384,
|
||||||
|
_norm(keccak512.name): keccak512,
|
||||||
|
_norm(md2.name): md2,
|
||||||
|
_norm(md4.name): md4,
|
||||||
|
_norm(md5.name): md5,
|
||||||
|
_norm(ripemd128.name): ripemd128,
|
||||||
|
_norm(ripemd160.name): ripemd160,
|
||||||
|
_norm(ripemd256.name): ripemd256,
|
||||||
|
_norm(ripemd320.name): ripemd320,
|
||||||
|
_norm(sha1.name): sha1,
|
||||||
|
_norm(sha3_224.name): sha3_224,
|
||||||
|
_norm(sha3_256.name): sha3_256,
|
||||||
|
_norm(sha3_384.name): sha3_384,
|
||||||
|
_norm(sha3_512.name): sha3_512,
|
||||||
|
_norm('SHA3'): sha3_512,
|
||||||
|
_norm(sha224.name): sha224,
|
||||||
|
_norm(sha256.name): sha256,
|
||||||
|
_norm('SHA2'): sha256,
|
||||||
|
_norm(sha384.name): sha384,
|
||||||
|
_norm(sha512.name): sha512,
|
||||||
|
_norm(sha512t224.name): sha512t224,
|
||||||
|
_norm(sha512t256.name): sha512t256,
|
||||||
|
_norm(shake128_128.name): shake128_128,
|
||||||
|
_norm(shake128_160.name): shake128_160,
|
||||||
|
_norm(shake128_224.name): shake128_224,
|
||||||
|
_norm(shake128_256.name): shake128_256,
|
||||||
|
_norm('SHAKE-128'): shake128_256,
|
||||||
|
_norm(shake128_384.name): shake128_384,
|
||||||
|
_norm(shake128_512.name): shake128_512,
|
||||||
|
_norm(shake256_128.name): shake256_128,
|
||||||
|
_norm(shake256_160.name): shake256_160,
|
||||||
|
_norm(shake256_224.name): shake256_224,
|
||||||
|
_norm(shake256_256.name): shake256_256,
|
||||||
|
_norm(shake256_384.name): shake256_384,
|
||||||
|
_norm(shake256_512.name): shake256_512,
|
||||||
|
_norm('SHAKE-512'): shake256_512,
|
||||||
|
_norm(sm3.name): sm3,
|
||||||
|
_norm(xxh3.name): xxh3,
|
||||||
|
_norm('XXH3-64'): xxh3,
|
||||||
|
_norm(xxh32.name): xxh32,
|
||||||
|
_norm(xxh64.name): xxh64,
|
||||||
|
_norm(xxh128.name): xxh128,
|
||||||
|
_norm('XXH3-128'): xxh128,
|
||||||
|
});
|
||||||
|
_hash.addAll(_blockHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A registry to find a block hash algorithm by name
|
||||||
|
@Deprecated('It will be removed in 2.0.0')
|
||||||
|
class BlockHashRegistry {
|
||||||
|
/// Find a [BlockHashBase] algorithm given a string name
|
||||||
|
static BlockHashBase? lookup(String name) {
|
||||||
|
_buildRegistry();
|
||||||
|
return _blockHash[_norm(name)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a new [BlockHashBase] algorithm on the fly given a string name
|
||||||
|
static void register(BlockHashBase algo, [String? name]) {
|
||||||
|
_buildRegistry();
|
||||||
|
name = _norm(name ?? algo.name);
|
||||||
|
_blockHash[name] = algo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A registry to find a hash algorithm by name
|
||||||
|
@Deprecated('It will be removed in 2.0.0')
|
||||||
|
class HashRegistry {
|
||||||
|
/// Find a [HashBase] algorithm given a string name
|
||||||
|
static HashBase? lookup(String name) {
|
||||||
|
_buildRegistry();
|
||||||
|
return _hash[_norm(name)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a new [HashBase] algorithm on the fly given a string name
|
||||||
|
static void register(HashBase algo, [String? name]) {
|
||||||
|
_buildRegistry();
|
||||||
|
name = _norm(name ?? algo.name);
|
||||||
|
_hash[name] = algo;
|
||||||
|
if (algo is BlockHashBase) {
|
||||||
|
_blockHash[name] = algo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
hashlib/lib/src/crc16.dart
Normal file
52
hashlib/lib/src/crc16.dart
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/algorithms/crc/crc16.dart';
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
export 'package:hashlib/src/algorithms/crc/crc16.dart'
|
||||||
|
show CRC16Hash, CRC16Params;
|
||||||
|
|
||||||
|
/// A CRC or cyclic redundancy check is code commonly used for error detection
|
||||||
|
/// and correction of digital data. This generates a 16-bit number as output.
|
||||||
|
///
|
||||||
|
/// **WARNING: It should not be used for cryptographic purposes.**
|
||||||
|
const crc16 = CRC16(CRC16Params.ibm);
|
||||||
|
|
||||||
|
/// CRC-16 code generator
|
||||||
|
class CRC16 extends HashBase {
|
||||||
|
final CRC16Params params;
|
||||||
|
|
||||||
|
/// Create a instance for generating CRC-16 hashes
|
||||||
|
const CRC16(this.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => "CRC-16/${params.name}";
|
||||||
|
|
||||||
|
@override
|
||||||
|
CRC16Hash createSink() =>
|
||||||
|
params.reversed ? CRC16ReverseHash(params) : CRC16NormalHash(params);
|
||||||
|
|
||||||
|
/// Gets the CRC-16 code for a String
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [input] is the string to hash
|
||||||
|
/// - The [encoding] is the encoding to use. Default is `input.codeUnits`
|
||||||
|
int code(String input, [Encoding? encoding]) =>
|
||||||
|
string(input, encoding).number();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the CRC-16 value.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [input] is the string to hash
|
||||||
|
/// - The [encoding] is the encoding to use. Default is `input.codeUnits`
|
||||||
|
/// - The [params] is the parameters to use. Default: [CRC16Params.ansi]
|
||||||
|
int crc16code(
|
||||||
|
String input, {
|
||||||
|
Encoding? encoding,
|
||||||
|
CRC16Params params = CRC16Params.ansi,
|
||||||
|
}) =>
|
||||||
|
CRC16(params).code(input, encoding);
|
||||||
55
hashlib/lib/src/crc32.dart
Normal file
55
hashlib/lib/src/crc32.dart
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) 2023, Sudipto Chandra
|
||||||
|
// All rights reserved. Check LICENSE file for details.
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:hashlib/src/algorithms/crc/crc32.dart';
|
||||||
|
import 'package:hashlib/src/core/hash_base.dart';
|
||||||
|
|
||||||
|
export 'package:hashlib/src/algorithms/crc/crc32.dart'
|
||||||
|
show CRC32Hash, CRC32Params;
|
||||||
|
|
||||||
|
/// A CRC-32 code generator with **IEEE** 802.3 CRC-32 polynomial.
|
||||||
|
///
|
||||||
|
/// A CRC or cyclic redundancy check is code commonly used for error detection
|
||||||
|
/// and correction of digital data. This generates a 32-bit number as output.
|
||||||
|
///
|
||||||
|
/// **WARNING: It should not be used for cryptographic purposes.**
|
||||||
|
const crc32 = CRC32(CRC32Params.ieee);
|
||||||
|
|
||||||
|
/// CRC-32 code generator
|
||||||
|
class CRC32 extends HashBase {
|
||||||
|
final CRC32Params params;
|
||||||
|
|
||||||
|
/// Create a instance for generating CRC-32 hashes
|
||||||
|
const CRC32(this.params);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => "CRC-32/${params.name}";
|
||||||
|
|
||||||
|
@override
|
||||||
|
CRC32Hash createSink() =>
|
||||||
|
params.reversed ? CRC32ReverseHash(params) : CRC32NormalHash(params);
|
||||||
|
|
||||||
|
/// Gets the CRC-32 code for a String
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [input] is the string to hash
|
||||||
|
/// - The [encoding] is the encoding to use. Default is `input.codeUnits`
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
int code(String input, [Encoding? encoding]) =>
|
||||||
|
string(input, encoding).number();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the CRC-32 value.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [input] is the string to hash
|
||||||
|
/// - The [encoding] is the encoding to use. Default is `input.codeUnits`
|
||||||
|
/// - The [params] is the parameters to use. Default: [CRC32Params.ieee]
|
||||||
|
int crc32code(
|
||||||
|
String input, {
|
||||||
|
Encoding? encoding,
|
||||||
|
CRC32Params params = CRC32Params.ieee,
|
||||||
|
}) =>
|
||||||
|
CRC32(params).code(input, encoding);
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue