twonly-app-dependencies/hashlib/lib/src/algorithms/sha2/sha2_1024_32bit.dart
2025-12-07 16:10:41 +01:00

305 lines
9.7 KiB
Dart

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