add dependencies

This commit is contained in:
otsmr 2025-12-07 16:10:41 +01:00
parent 6b82890b13
commit de6c8c4e36
1560 changed files with 309395 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.venv

11
adaptive_number/LICENSE Normal file
View 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.

View file

@ -0,0 +1,6 @@
/// Support for doing something awesome.
///
/// More dartdocs go here.
library adaptive_number;
export 'src/number.dart';

View 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);

View 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));

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

View 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');

View 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

View 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
View 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
View 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
View 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.

View file

@ -0,0 +1,4 @@
library dots_indicator;
export 'src/dots_indicator.dart' show DotsIndicator;
export 'src/dots_decorator.dart' show DotsDecorator;

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

View 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,
);
}
}

View 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
View 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.

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

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

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

View 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

View 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();
}

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

View 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
View 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
View 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
View 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
View 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';

View 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();
}

View 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,
]);
}
}

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

View 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,
]);
}
}

View 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,
]);
}
}

View 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(),
);
}
}

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

View 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,
];

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

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

View 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';

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

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

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

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

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

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

View 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();
}
}

View 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,
);
}

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

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

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

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

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

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

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

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

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

View 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';

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

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

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

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

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

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

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

View 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
],
);
}

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

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

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

View 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,
);
}

View 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,
);
}

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

View 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';

View 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');
}
}

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

View 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';

View 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');
}
}

View 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,
]);
}
}

View 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';

View 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,
]);
}
}

View 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,
]);
}
}

View 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';

View 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');
}
}

View 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
View 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);

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

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

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

View 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]);
}

View 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');
}
}

View 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();
}
}
}

View 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');
}
}

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

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

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

View 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');
}

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

View 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);

View 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