323 lines
8.4 KiB
Dart
323 lines
8.4 KiB
Dart
// This file has been migrated.
|
|
|
|
library impl.mac.poly1305;
|
|
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:pointycastle/src/platform_check/platform_check.dart';
|
|
|
|
import '../api.dart';
|
|
import '../src/impl/base_mac.dart';
|
|
import '../src/registry/registry.dart';
|
|
import '../src/ufixnum.dart';
|
|
import '../src/utils.dart';
|
|
|
|
class Poly1305 extends BaseMac {
|
|
static const R_MASK_LOW_2 = 0xFC;
|
|
static const R_MASK_HIGH_4 = 0x0F;
|
|
|
|
Poly1305() {
|
|
Platform.instance.assertFullWidthInteger();
|
|
cipher = null;
|
|
}
|
|
|
|
Poly1305.withCipher(this.cipher) {
|
|
Platform.instance.assertFullWidthInteger();
|
|
if (cipher!.blockSize != BLOCK_SIZE) {
|
|
throw ArgumentError('Poly1305 requires a 128 bit block cipher.');
|
|
}
|
|
}
|
|
|
|
// ignore: non_constant_identifier_names
|
|
static final FactoryConfig factoryConfig = DynamicFactoryConfig.suffix(
|
|
Mac,
|
|
'/Poly1305',
|
|
(_, final Match match) => () {
|
|
var cipher = BlockCipher(match.group(1)!);
|
|
return Poly1305.withCipher(cipher);
|
|
},
|
|
);
|
|
|
|
static void clamp(Uint8List key) {
|
|
key[3] &= R_MASK_HIGH_4;
|
|
key[7] &= R_MASK_HIGH_4;
|
|
key[11] &= R_MASK_HIGH_4;
|
|
key[15] &= R_MASK_HIGH_4;
|
|
|
|
key[4] &= R_MASK_LOW_2;
|
|
key[8] &= R_MASK_LOW_2;
|
|
key[12] &= R_MASK_LOW_2;
|
|
}
|
|
|
|
static bool checkKey(Uint8List key) {
|
|
var c1 = checkMask(key[3], R_MASK_HIGH_4);
|
|
var c2 = checkMask(key[7], R_MASK_HIGH_4);
|
|
var c3 = checkMask(key[11], R_MASK_HIGH_4);
|
|
var c4 = checkMask(key[15], R_MASK_HIGH_4);
|
|
|
|
var c5 = checkMask(key[4], R_MASK_LOW_2);
|
|
var c6 = checkMask(key[8], R_MASK_LOW_2);
|
|
var c7 = checkMask(key[12], R_MASK_LOW_2);
|
|
|
|
return !(c1 || c2 || c3 || c4 || c5 || c6 || c7);
|
|
}
|
|
|
|
static bool checkMask(int b, int mask) {
|
|
if (b & not32(mask) != 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
String get algorithmName =>
|
|
cipher == null ? 'Poly1305' : '${cipher!.algorithmName}/Poly1305';
|
|
|
|
@override
|
|
int get macSize => BLOCK_SIZE;
|
|
|
|
static const BLOCK_SIZE = 16;
|
|
|
|
BlockCipher? cipher;
|
|
|
|
final Uint8List singleByte = Uint8List(1);
|
|
|
|
late int r0, r1, r2, r3, r4;
|
|
|
|
late int s1, s2, s3, s4;
|
|
|
|
late int k0, k1, k2, k3;
|
|
|
|
final Uint8List currentBlock = Uint8List(BLOCK_SIZE);
|
|
|
|
int currentBlockOffset = 0;
|
|
|
|
late int h0, h1, h2, h3, h4;
|
|
|
|
@override
|
|
void init(CipherParameters params) {
|
|
Uint8List? nonce;
|
|
|
|
if (cipher != null) {
|
|
if (params is! ParametersWithIV) {
|
|
throw ArgumentError(
|
|
'Poly1305 requires an IV when used with a block cipher.');
|
|
}
|
|
|
|
nonce = params.iv;
|
|
params = params.parameters!;
|
|
}
|
|
|
|
if (params is! KeyParameter) {
|
|
throw ArgumentError('Poly1305 requires a key.');
|
|
}
|
|
|
|
if (!checkKey(params.key)) clamp(params.key);
|
|
|
|
setKey(params.key, nonce);
|
|
|
|
reset();
|
|
}
|
|
|
|
void setKey(Uint8List key, Uint8List? nonce) {
|
|
if (key.length != 32) throw ArgumentError('Poly1305 key must be 256 bits.');
|
|
if (cipher != null && (nonce == null || nonce.length != BLOCK_SIZE)) {
|
|
throw ArgumentError('Poly1305-AES requires a 128 bit IV.');
|
|
}
|
|
|
|
var keyByteData = ByteData.view(key.buffer, key.offsetInBytes, key.length);
|
|
var t0 = unpack32(keyByteData, 0, Endian.little);
|
|
var t1 = unpack32(keyByteData, 4, Endian.little);
|
|
var t2 = unpack32(keyByteData, 8, Endian.little);
|
|
var t3 = unpack32(keyByteData, 12, Endian.little);
|
|
|
|
r0 = t0 & (0x03FFFFFF);
|
|
r1 = (cshiftr32(t0, 26) | shiftl32(t1, 6)) & 0x03FFFF03;
|
|
r2 = (cshiftr32(t1, 20) | shiftl32(t2, 12)) & 0x03FFC0FF;
|
|
r3 = (cshiftr32(t2, 14) | shiftl32(t3, 18)) & 0x03F03FFF;
|
|
r4 = (cshiftr32(t3, 8)) & 0x000FFFFF;
|
|
|
|
s1 = r1 * 5;
|
|
s2 = r2 * 5;
|
|
s3 = r3 * 5;
|
|
s4 = r4 * 5;
|
|
|
|
Uint8List kBytes;
|
|
int kOff;
|
|
if (cipher == null) {
|
|
kBytes = key;
|
|
kOff = BLOCK_SIZE;
|
|
} else {
|
|
kBytes = Uint8List(BLOCK_SIZE);
|
|
kOff = 0;
|
|
|
|
cipher!.init(true, KeyParameter.offset(key, BLOCK_SIZE, BLOCK_SIZE));
|
|
cipher!.processBlock(nonce!, 0, kBytes, 0);
|
|
}
|
|
|
|
var kByteData =
|
|
ByteData.view(kBytes.buffer, kBytes.offsetInBytes, kBytes.length);
|
|
k0 = unpack32(kByteData, kOff, Endian.little);
|
|
k1 = unpack32(kByteData, kOff + 4, Endian.little);
|
|
k2 = unpack32(kByteData, kOff + 8, Endian.little);
|
|
k3 = unpack32(kByteData, kOff + 12, Endian.little);
|
|
}
|
|
|
|
@override
|
|
void updateByte(final int inp) {
|
|
singleByte[0] = inp;
|
|
update(singleByte, 0, 1);
|
|
}
|
|
|
|
@override
|
|
void update(final Uint8List inp, final int inOff, final int len) {
|
|
var copied = 0;
|
|
while (len > copied) {
|
|
if (currentBlockOffset == BLOCK_SIZE) {
|
|
processBlock();
|
|
currentBlockOffset = 0;
|
|
}
|
|
|
|
var toCopy = (len - copied) > (BLOCK_SIZE - currentBlockOffset)
|
|
? (BLOCK_SIZE - currentBlockOffset)
|
|
: (len - copied);
|
|
arrayCopy(inp, copied + inOff, currentBlock, currentBlockOffset, toCopy);
|
|
copied += toCopy;
|
|
currentBlockOffset += toCopy;
|
|
}
|
|
}
|
|
|
|
void processBlock() {
|
|
// TODO Calculation varied between web and native.
|
|
if (currentBlockOffset < BLOCK_SIZE) {
|
|
currentBlock[currentBlockOffset] = 1;
|
|
for (var i = currentBlockOffset + 1; i < BLOCK_SIZE; i++) {
|
|
currentBlock[i] = 0;
|
|
}
|
|
}
|
|
|
|
final t0 = unpack32(currentBlock, 0, Endian.little);
|
|
final t1 = unpack32(currentBlock, 4, Endian.little);
|
|
final t2 = unpack32(currentBlock, 8, Endian.little);
|
|
final t3 = unpack32(currentBlock, 12, Endian.little);
|
|
|
|
h0 += t0 & 0x3ffffff;
|
|
h1 += uRS((t1 << 32) | t0, 26) & 0x3ffffff;
|
|
h2 += uRS((t2 << 32) | t1, 20) & 0x3ffffff;
|
|
h3 += uRS((t3 << 32) | t2, 14) & 0x3ffffff;
|
|
h4 += uRS(t3, 8);
|
|
|
|
if (currentBlockOffset == BLOCK_SIZE) {
|
|
h4 += shiftl32(1, 24);
|
|
}
|
|
|
|
var tp0 = h0 * r0 + h1 * s4 + h2 * s3 + h3 * s2 + h4 * s1;
|
|
var tp1 = h0 * r1 + h1 * r0 + h2 * s4 + h3 * s3 + h4 * s2;
|
|
var tp2 = h0 * r2 + h1 * r1 + h2 * r0 + h3 * s4 + h4 * s3;
|
|
var tp3 = h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * s4;
|
|
var tp4 = h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
|
|
|
|
h0 = (tp0 & 0xffffffff) & 0x3ffffff;
|
|
tp1 += uRS(tp0, 26);
|
|
h1 = (tp1 & 0xffffffff) & 0x3ffffff;
|
|
tp2 += uRS(tp1, 26);
|
|
h2 = (tp2 & 0xffffffff) & 0x3ffffff;
|
|
tp3 += uRS(tp2, 26);
|
|
h3 = (tp3 & 0xffffffff) & 0x3ffffff;
|
|
tp4 += uRS(tp3, 26);
|
|
h4 = (tp4 & 0xffffffff) & 0x3ffffff;
|
|
|
|
h0 += (uRS(tp4, 26) & 0xffffffff) * 5;
|
|
h1 += cshiftr32(h0, 26);
|
|
h0 &= 0x3ffffff;
|
|
}
|
|
|
|
@override
|
|
int doFinal(Uint8List out, final int outOff) {
|
|
if (outOff + BLOCK_SIZE > out.length) {
|
|
throw ArgumentError('Output buffer is too short.');
|
|
}
|
|
|
|
if (currentBlockOffset > 0) {
|
|
processBlock();
|
|
}
|
|
|
|
h1 += cshiftr32(h0, 26);
|
|
h0 &= 0x3ffffff;
|
|
h2 += cshiftr32(h1, 26);
|
|
h1 &= 0x3ffffff;
|
|
h3 += cshiftr32(h2, 26);
|
|
h2 &= 0x3ffffff;
|
|
h4 += cshiftr32(h3, 26);
|
|
h3 &= 0x3ffffff;
|
|
h0 += cshiftr32(h4, 26) * 5;
|
|
h4 &= 0x3ffffff;
|
|
h1 += cshiftr32(h0, 26);
|
|
h0 &= 0x3ffffff;
|
|
|
|
int g0, g1, g2, g3, g4, b;
|
|
g0 = sum32(h0, 5);
|
|
b = cshiftr32(g0, 26);
|
|
g0 &= 0x3ffffff;
|
|
g1 = sum32(h1, b);
|
|
b = cshiftr32(g1, 26);
|
|
g1 &= 0x3ffffff;
|
|
g2 = sum32(h2, b);
|
|
b = cshiftr32(g2, 26);
|
|
g2 &= 0x3ffffff;
|
|
g3 = sum32(h3, b);
|
|
b = cshiftr32(g3, 26);
|
|
g3 &= 0x3ffffff;
|
|
g4 = sum32(h4, b) - shiftl32(1, 26);
|
|
|
|
b = cshiftr32(g4, 31) - 1;
|
|
var nb = not32(b);
|
|
h0 = (h0 & nb) | (g0 & b);
|
|
h1 = (h1 & nb) | (g1 & b);
|
|
h2 = (h2 & nb) | (g2 & b);
|
|
h3 = (h3 & nb) | (g3 & b);
|
|
h4 = (h4 & nb) | (g4 & b);
|
|
|
|
int f0, f1, f2, f3;
|
|
f0 = (h0 | shiftl32(h1, 26)) + k0;
|
|
f1 = (cshiftr32(h1, 6) | shiftl32(h2, 20)) + k1;
|
|
f2 = (cshiftr32(h2, 12) | shiftl32(h3, 14)) + k2;
|
|
f3 = (cshiftr32(h3, 18) | shiftl32(h4, 8)) + k3;
|
|
|
|
var outByte = ByteData.view(out.buffer, out.offsetInBytes, out.length);
|
|
pack32(f0 & 0xffffffff, outByte, outOff, Endian.little);
|
|
f1 += uRS(f0, 32);
|
|
pack32(f1 & 0xffffffff, outByte, outOff + 4, Endian.little);
|
|
f2 += uRS(f1, 32);
|
|
pack32(f2 & 0xffffffff, outByte, outOff + 8, Endian.little);
|
|
f3 += uRS(f2, 32);
|
|
pack32(f3 & 0xffffffff, outByte, outOff + 12, Endian.little);
|
|
//End come back here chunk
|
|
|
|
out = outByte.buffer.asUint8List();
|
|
|
|
reset();
|
|
return BLOCK_SIZE;
|
|
}
|
|
|
|
@override
|
|
void reset() {
|
|
currentBlockOffset = 0;
|
|
|
|
h0 = 0;
|
|
h1 = 0;
|
|
h2 = 0;
|
|
h3 = 0;
|
|
h4 = 0;
|
|
}
|
|
}
|
|
|
|
int uRS(int x, int n) {
|
|
return x >= 0 ? x >> n : ~(~x >> n);
|
|
}
|
|
|
|
/*
|
|
int uRS(int x, int n) {
|
|
return (x >= 0) ? x >> (64 - n) : ~(~x >> (64 - n));
|
|
}
|
|
*/
|