{ authData ? [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ], counter ? [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ], key, text }: let inherit(builtins) bitAnd bitXor concatStringsSep elemAt foldl' map trace deepSeq stringLength substring; inherit(import ./ascii.nix) toDWords hex; encryptAES = import ./aes.nix; trace' = x: trace (deepSeq x x); traceHex = value: trace' (map hex value); key' = toDWords key; counter' = toDWords counter; encryptChunk = encryptAES key'; shift1 = 256; shift2 = 256 * 256; shift3 = 256 * 256 * 256; byteAt = dword: index: if index == 0 then bitAnd dword 255 else if index == 1 then bitAnd (dword / shift1) 255 else if index == 2 then bitAnd (dword / shift2) 255 else if index == 3 then dword / shift3 else null; H = encryptChunk [ 0 0 0 0 ]; max = 256 * 256 * 256 * 256 - 1; increment = counter: let a = elemAt counter 0; b = elemAt counter 1; c = elemAt counter 2; d = elemAt counter 3; inc = value: let a = byteAt value 0; b = byteAt value 1; c = byteAt value 2; d = byteAt value 3; in if d == 255 then if c == 255 then if b == 255 then if a == 255 then { value = 0; carry = true; } else { value = bitAnd value (shift1 - 1) + 1; carry = false; } else { value = bitAnd value (shift2 - 1) + shift1; carry = false; } else { value = bitAnd value (shift3 - 1) + shift2; carry = false; } else { value = value + shift3; carry = false; }; a' = inc a; b' = inc b; c' = inc c; d' = inc d; in if !d'.carry then [ a b c d'.value ] else if !c'.carry then [ a b c'.value 0 ] else if !b'.carry then [ a b'.value 0 0 ] else if !a'.carry then [ a'.value 0 0 0 ] else [ 0 0 0 0 ]; multiply = authData: [ 0 0 0 0 ]; # ghash encrypted = foldl' ( { auth, authData, cipherText, counter, index, key }: plain: let auth' = if index == 3 then elemAt (multiply authData) else auth; authData' = if index == 3 then [] else authData; counter' = if index == 3 then increment counter else counter; index' = if index == 3 then 0 else index + 1; key' = if index == 3 then elemAt (encryptChunk counter) else key; cipher = bitXor plain ( key' index' ); in # trace "${hex plain} ^ ${hex (key' index')} = ${hex cipher}" { auth = auth'; authData = authData ++ [ (bitXor cipher (auth' index')) ]; cipherText = cipherText ++ [ cipher ]; counter = counter'; index = index'; key = key'; } ) { auth = elemAt (toDWords authData); authData = []; cipherText = []; counter = counter'; index = 3; key = null; } (toDWords text); in # traceHex key' substring 0 (stringLength text) (concatStringsSep "" (map hex encrypted.cipherText))