blob: 9ee4b516422ac7af8026c355f866e8a16b0d0456 [file] [log] [blame] [edit]
// 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));
}
*/