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