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