Skip to main content

src/packkit@checksum.erl

-module(packkit@checksum).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/checksum.gleam").
-export([sha256_init/0, adler32/1, adler32_continue/2, crc32/1, crc32_continue/2, crc32c/1, bzip2_crc32/1, crc64_xz/1, snappy_mask/1, sha256_finalize/1, sha256_update/2, sha256/1, sha1/1, hmac_sha1/2, pbkdf2_hmac_sha1/4]).
-export_type([sha256_state/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(
    " Adler-32 and CRC-32 checksums used by zlib (RFC 1950) and gzip\n"
    " (RFC 1952).\n"
    "\n"
    " The implementations are pure Gleam and behave identically on the\n"
    " Erlang and JavaScript targets.\n"
).

-opaque sha256_state() :: {sha256_state,
        integer(),
        integer(),
        integer(),
        integer(),
        integer(),
        integer(),
        integer(),
        integer(),
        bitstring(),
        integer()}.

-file("src/packkit/checksum.gleam", 52).
-spec adler32_chunk(bitstring(), integer(), integer(), integer()) -> {bitstring(),
    integer(),
    integer()}.
adler32_chunk(Data, Remaining, S1, S2) ->
    case {Remaining, Data} of
        {0, _} ->
            {Data, S1, S2};

        {_, <<B, Rest/binary>>} ->
            S1@1 = S1 + B,
            S2@1 = S2 + S1@1,
            adler32_chunk(Rest, Remaining - 1, S1@1, S2@1);

        {_, _} ->
            {Data, S1, S2}
    end.

-file("src/packkit/checksum.gleam", 69).
-spec combine_adler(integer(), integer()) -> integer().
combine_adler(S1, S2) ->
    erlang:'bor'(erlang:'bsl'(S2, 16), S1).

-file("src/packkit/checksum.gleam", 184).
-spec crc64_nibble_low(integer()) -> integer().
crc64_nibble_low(Index) ->
    case Index of
        0 ->
            16#00000000;

        1 ->
            16#51336649;

        2 ->
            16#A266CC92;

        3 ->
            16#F355AADB;

        4 ->
            16#EBC387A1;

        5 ->
            16#BAF0E1E8;

        6 ->
            16#49A54B33;

        7 ->
            16#18962D7A;

        8 ->
            16#D7870F42;

        9 ->
            16#86B4690B;

        10 ->
            16#75E1C3D0;

        11 ->
            16#24D2A599;

        12 ->
            16#3C4488E3;

        13 ->
            16#6D77EEAA;

        14 ->
            16#9E224471;

        _ ->
            16#CF112238
    end.

-file("src/packkit/checksum.gleam", 205).
-spec crc64_nibble_high(integer()) -> integer().
crc64_nibble_high(Index) ->
    case Index of
        0 ->
            16#00000000;

        1 ->
            16#7D9BA138;

        2 ->
            16#FB374270;

        3 ->
            16#86ACE348;

        4 ->
            16#64B62BCA;

        5 ->
            16#192D8AF2;

        6 ->
            16#9F8169BA;

        7 ->
            16#E21AC882;

        8 ->
            16#C96C5795;

        9 ->
            16#B4F7F6AD;

        10 ->
            16#325B15E5;

        11 ->
            16#4FC0B4DD;

        12 ->
            16#ADDA7C5F;

        13 ->
            16#D041DD67;

        14 ->
            16#56ED3E2F;

        _ ->
            16#2B769F17
    end.

-file("src/packkit/checksum.gleam", 255).
-spec crc32_nibble(integer()) -> integer().
crc32_nibble(Index) ->
    case Index of
        0 ->
            16#00000000;

        1 ->
            16#1DB71064;

        2 ->
            16#3B6E20C8;

        3 ->
            16#26D930AC;

        4 ->
            16#76DC4190;

        5 ->
            16#6B6B51F4;

        6 ->
            16#4DB26158;

        7 ->
            16#5005713C;

        8 ->
            16#EDB88320;

        9 ->
            16#F00F9344;

        10 ->
            16#D6D6A3E8;

        11 ->
            16#CB61B38C;

        12 ->
            16#9B64C2B0;

        13 ->
            16#86D3D2D4;

        14 ->
            16#A00AE278;

        _ ->
            16#BDBDF21C
    end.

-file("src/packkit/checksum.gleam", 249).
-spec step4(integer()) -> integer().
step4(Crc) ->
    Low = erlang:'band'(Crc, 15),
    Shifted = erlang:'bsr'(Crc, 4),
    erlang:'bxor'(crc32_nibble(Low), Shifted).

-file("src/packkit/checksum.gleam", 237).
-spec crc32_loop(bitstring(), integer()) -> integer().
crc32_loop(Data, Crc) ->
    case Data of
        <<B, Rest/binary>> ->
            Crc@1 = erlang:'bxor'(Crc, B),
            Crc@2 = step4(Crc@1),
            Crc@3 = step4(Crc@2),
            crc32_loop(Rest, Crc@3);

        _ ->
            Crc
    end.

-file("src/packkit/checksum.gleam", 294).
-spec crc32c_nibble(integer()) -> integer().
crc32c_nibble(Index) ->
    case Index of
        0 ->
            16#00000000;

        1 ->
            16#105EC76F;

        2 ->
            16#20BD8EDE;

        3 ->
            16#30E349B1;

        4 ->
            16#417B1DBC;

        5 ->
            16#5125DAD3;

        6 ->
            16#61C69362;

        7 ->
            16#7198540D;

        8 ->
            16#82F63B78;

        9 ->
            16#92A8FC17;

        10 ->
            16#A24BB5A6;

        11 ->
            16#B21572C9;

        12 ->
            16#C38D26C4;

        13 ->
            16#D3D3E1AB;

        14 ->
            16#E330A81A;

        _ ->
            16#F36E6F75
    end.

-file("src/packkit/checksum.gleam", 288).
-spec step4_c(integer()) -> integer().
step4_c(Crc) ->
    Low = erlang:'band'(Crc, 15),
    Shifted = erlang:'bsr'(Crc, 4),
    erlang:'bxor'(crc32c_nibble(Low), Shifted).

-file("src/packkit/checksum.gleam", 276).
-spec crc32c_loop(bitstring(), integer()) -> integer().
crc32c_loop(Data, Crc) ->
    case Data of
        <<B, Rest/binary>> ->
            Crc@1 = erlang:'bxor'(Crc, B),
            Crc@2 = step4_c(Crc@1),
            Crc@3 = step4_c(Crc@2),
            crc32c_loop(Rest, Crc@3);

        _ ->
            Crc
    end.

-file("src/packkit/checksum.gleam", 315).
-spec mod(integer(), integer()) -> integer().
mod(Value, Divisor) ->
    Value - ((case Divisor of
        0 -> 0;
        Gleam@denominator -> Value div Gleam@denominator
    end) * Divisor).

-file("src/packkit/checksum.gleam", 348).
?DOC(
    " Fresh SHA-256 state using the FIPS 180-4 ยง5.3.3 initial hash\n"
    " constants.\n"
).
-spec sha256_init() -> sha256_state().
sha256_init() ->
    {sha256_state,
        16#6A09E667,
        16#BB67AE85,
        16#3C6EF372,
        16#A54FF53A,
        16#510E527F,
        16#9B05688C,
        16#1F83D9AB,
        16#5BE0CD19,
        <<>>,
        0}.

-file("src/packkit/checksum.gleam", 500).
-spec pad_zeros(integer(), bitstring()) -> bitstring().
pad_zeros(Count, Acc) ->
    case Count of
        0 ->
            Acc;

        _ ->
            pad_zeros(Count - 1, gleam_stdlib:bit_array_concat([Acc, <<0>>]))
    end.

-file("src/packkit/checksum.gleam", 487).
?DOC(
    " Pad `data` to a multiple of 64 bytes following FIPS 180-4 ยง5.1.1:\n"
    " append `0x80`, then enough `0x00` to leave 8 bytes for the\n"
    " 64-bit big-endian bit-length.\n"
).
-spec sha256_pad(bitstring()) -> bitstring().
sha256_pad(Data) ->
    Len = erlang:byte_size(Data),
    Bit_len = Len * 8,
    Mod_len = mod(Len + 9, 64),
    Zeros = case Mod_len of
        0 ->
            0;

        N ->
            64 - N
    end,
    Padding = pad_zeros(Zeros, <<>>),
    gleam_stdlib:bit_array_concat(
        [Data, <<16#80>>, Padding, <<Bit_len:64/big>>]
    ).

-file("src/packkit/checksum.gleam", 606).
-spec nth_from_head(list(integer()), integer()) -> integer().
nth_from_head(List_value, Index) ->
    case {List_value, Index} of
        {[Head | _], 0} ->
            Head;

        {[_ | Rest], N} ->
            nth_from_head(Rest, N - 1);

        {[], _} ->
            0
    end.

-file("src/packkit/checksum.gleam", 698).
-spec sha256_k(integer()) -> integer().
sha256_k(Round) ->
    case Round of
        0 ->
            16#428A2F98;

        1 ->
            16#71374491;

        2 ->
            16#B5C0FBCF;

        3 ->
            16#E9B5DBA5;

        4 ->
            16#3956C25B;

        5 ->
            16#59F111F1;

        6 ->
            16#923F82A4;

        7 ->
            16#AB1C5ED5;

        8 ->
            16#D807AA98;

        9 ->
            16#12835B01;

        10 ->
            16#243185BE;

        11 ->
            16#550C7DC3;

        12 ->
            16#72BE5D74;

        13 ->
            16#80DEB1FE;

        14 ->
            16#9BDC06A7;

        15 ->
            16#C19BF174;

        16 ->
            16#E49B69C1;

        17 ->
            16#EFBE4786;

        18 ->
            16#0FC19DC6;

        19 ->
            16#240CA1CC;

        20 ->
            16#2DE92C6F;

        21 ->
            16#4A7484AA;

        22 ->
            16#5CB0A9DC;

        23 ->
            16#76F988DA;

        24 ->
            16#983E5152;

        25 ->
            16#A831C66D;

        26 ->
            16#B00327C8;

        27 ->
            16#BF597FC7;

        28 ->
            16#C6E00BF3;

        29 ->
            16#D5A79147;

        30 ->
            16#06CA6351;

        31 ->
            16#14292967;

        32 ->
            16#27B70A85;

        33 ->
            16#2E1B2138;

        34 ->
            16#4D2C6DFC;

        35 ->
            16#53380D13;

        36 ->
            16#650A7354;

        37 ->
            16#766A0ABB;

        38 ->
            16#81C2C92E;

        39 ->
            16#92722C85;

        40 ->
            16#A2BFE8A1;

        41 ->
            16#A81A664B;

        42 ->
            16#C24B8B70;

        43 ->
            16#C76C51A3;

        44 ->
            16#D192E819;

        45 ->
            16#D6990624;

        46 ->
            16#F40E3585;

        47 ->
            16#106AA070;

        48 ->
            16#19A4C116;

        49 ->
            16#1E376C08;

        50 ->
            16#2748774C;

        51 ->
            16#34B0BCB5;

        52 ->
            16#391C0CB3;

        53 ->
            16#4ED8AA4A;

        54 ->
            16#5B9CCA4F;

        55 ->
            16#682E6FF3;

        56 ->
            16#748F82EE;

        57 ->
            16#78A5636F;

        58 ->
            16#84C87814;

        59 ->
            16#8CC70208;

        60 ->
            16#90BEFFFA;

        61 ->
            16#A4506CEB;

        62 ->
            16#BEF9A3F7;

        _ ->
            16#C67178F2
    end.

-file("src/packkit/checksum.gleam", 948).
-spec xor_with_byte_loop(bitstring(), integer(), bitstring()) -> bitstring().
xor_with_byte_loop(Data, Mask, Acc) ->
    case Data of
        <<B, Rest/binary>> ->
            Mixed = erlang:'bxor'(B, Mask),
            xor_with_byte_loop(
                Rest,
                Mask,
                gleam_stdlib:bit_array_concat([Acc, <<Mixed>>])
            );

        _ ->
            Acc
    end.

-file("src/packkit/checksum.gleam", 944).
-spec xor_with_byte(bitstring(), integer()) -> bitstring().
xor_with_byte(Key_block, Mask) ->
    xor_with_byte_loop(Key_block, Mask, <<>>).

-file("src/packkit/checksum.gleam", 1042).
-spec xor_bit_arrays_loop(bitstring(), bitstring(), bitstring()) -> bitstring().
xor_bit_arrays_loop(Left, Right, Acc) ->
    case {Left, Right} of
        {<<L, L_rest/binary>>, <<R, R_rest/binary>>} ->
            Mixed = erlang:'bxor'(L, R),
            xor_bit_arrays_loop(
                L_rest,
                R_rest,
                gleam_stdlib:bit_array_concat([Acc, <<Mixed>>])
            );

        {_, _} ->
            Acc
    end.

-file("src/packkit/checksum.gleam", 1038).
-spec xor_bit_arrays(bitstring(), bitstring()) -> bitstring().
xor_bit_arrays(Left, Right) ->
    xor_bit_arrays_loop(Left, Right, <<>>).

-file("src/packkit/checksum.gleam", 36).
-spec adler32_loop(bitstring(), integer(), integer(), integer()) -> integer().
adler32_loop(Data, Remaining, S1, S2) ->
    case Remaining of
        0 ->
            combine_adler(S1, S2);

        _ ->
            K = case Remaining < 5552 of
                true ->
                    Remaining;

                false ->
                    5552
            end,
            {Rest, New_s1, New_s2} = adler32_chunk(Data, K, S1, S2),
            New_s1@1 = mod(New_s1, 65521),
            New_s2@1 = mod(New_s2, 65521),
            adler32_loop(Rest, Remaining - K, New_s1@1, New_s2@1)
    end.

-file("src/packkit/checksum.gleam", 23).
?DOC(
    " Compute the Adler-32 checksum of `data`, returning the canonical\n"
    " 32-bit value `(s2 << 16) | s1`.\n"
).
-spec adler32(bitstring()) -> integer().
adler32(Data) ->
    Length = erlang:byte_size(Data),
    adler32_loop(Data, Length, 1, 0).

-file("src/packkit/checksum.gleam", 29).
?DOC(" Continue an Adler-32 computation from a previous checksum value.\n").
-spec adler32_continue(integer(), bitstring()) -> integer().
adler32_continue(Previous, Data) ->
    S1 = erlang:'band'(Previous, 65535),
    S2 = erlang:'band'(erlang:'bsr'(Previous, 16), 65535),
    Length = erlang:byte_size(Data),
    adler32_loop(Data, Length, S1, S2).

-file("src/packkit/checksum.gleam", 75).
?DOC(
    " Compute the CRC-32 checksum of `data` using the IEEE 802.3 reflected\n"
    " polynomial `0xEDB88320` and the same final XOR as zlib's `crc32`.\n"
).
-spec crc32(bitstring()) -> integer().
crc32(Data) ->
    Crc = crc32_loop(Data, 4294967295),
    erlang:'band'(erlang:'bxor'(Crc, 4294967295), 4294967295).

-file("src/packkit/checksum.gleam", 81).
?DOC(" Continue a CRC-32 computation from a previous checksum value.\n").
-spec crc32_continue(integer(), bitstring()) -> integer().
crc32_continue(Previous, Data) ->
    Crc = erlang:'bxor'(erlang:'band'(Previous, 4294967295), 4294967295),
    Crc@1 = crc32_loop(Data, Crc),
    erlang:'band'(erlang:'bxor'(Crc@1, 4294967295), 4294967295).

-file("src/packkit/checksum.gleam", 91).
?DOC(
    " Compute the CRC-32C (Castagnoli) checksum of `data` using the\n"
    " reflected polynomial `0x82F63B78`.  CRC-32C underlies the masked\n"
    " checksums in Snappy's frame format.\n"
).
-spec crc32c(bitstring()) -> integer().
crc32c(Data) ->
    Crc = crc32c_loop(Data, 4294967295),
    erlang:'band'(erlang:'bxor'(Crc, 4294967295), 4294967295).

-file("src/packkit/checksum.gleam", 131).
-spec bzip2_step_bit(integer()) -> integer().
bzip2_step_bit(Crc) ->
    Shifted = erlang:'band'(erlang:'bsl'(Crc, 1), 4294967295),
    case erlang:'band'(Crc, 16#80000000) of
        0 ->
            Shifted;

        _ ->
            erlang:'bxor'(Shifted, 16#04C11DB7)
    end.

-file("src/packkit/checksum.gleam", 120).
-spec bzip2_step_byte(integer()) -> integer().
bzip2_step_byte(Crc) ->
    _pipe = bzip2_step_bit(Crc),
    _pipe@1 = bzip2_step_bit(_pipe),
    _pipe@2 = bzip2_step_bit(_pipe@1),
    _pipe@3 = bzip2_step_bit(_pipe@2),
    _pipe@4 = bzip2_step_bit(_pipe@3),
    _pipe@5 = bzip2_step_bit(_pipe@4),
    _pipe@6 = bzip2_step_bit(_pipe@5),
    bzip2_step_bit(_pipe@6).

-file("src/packkit/checksum.gleam", 107).
-spec bzip2_crc32_loop(bitstring(), integer()) -> integer().
bzip2_crc32_loop(Data, Crc) ->
    case Data of
        <<B, Rest/binary>> ->
            Crc@1 = begin
                _pipe = erlang:'bxor'(Crc, erlang:'bsl'(B, 24)),
                erlang:'band'(_pipe, 4294967295)
            end,
            Crc@2 = bzip2_step_byte(Crc@1),
            bzip2_crc32_loop(Rest, Crc@2);

        _ ->
            Crc
    end.

-file("src/packkit/checksum.gleam", 102).
?DOC(
    " Compute the bzip2 CRC-32 of `data`.\n"
    "\n"
    " bzip2 reuses the IEEE 802.3 polynomial `0x04C11DB7` but processes\n"
    " each byte MSB-first (without reflection) and XORs the final value\n"
    " with `0xFFFFFFFF`, matching the per-block and stream CRC fields\n"
    " that appear in `.bz2` files.\n"
).
-spec bzip2_crc32(bitstring()) -> integer().
bzip2_crc32(Data) ->
    Crc = bzip2_crc32_loop(Data, 4294967295),
    erlang:'band'(erlang:'bxor'(Crc, 4294967295), 4294967295).

-file("src/packkit/checksum.gleam", 168).
-spec crc64_step4(integer(), integer()) -> {integer(), integer()}.
crc64_step4(Low, High) ->
    Nibble = erlang:'band'(Low, 15),
    New_low = erlang:'bor'(
        erlang:'bsr'(Low, 4),
        erlang:'band'(erlang:'bsl'(High, 28), 4294967295)
    ),
    New_high = erlang:'bsr'(High, 4),
    {erlang:'bxor'(New_low, crc64_nibble_low(Nibble)),
        erlang:'bxor'(New_high, crc64_nibble_high(Nibble))}.

-file("src/packkit/checksum.gleam", 156).
-spec crc64_loop(bitstring(), integer(), integer()) -> {integer(), integer()}.
crc64_loop(Data, Low, High) ->
    case Data of
        <<B, Rest/binary>> ->
            Low@1 = erlang:'bxor'(Low, B),
            {Low@2, High@1} = crc64_step4(Low@1, High),
            {Low@3, High@2} = crc64_step4(Low@2, High@1),
            crc64_loop(Rest, Low@3, High@2);

        _ ->
            {Low, High}
    end.

-file("src/packkit/checksum.gleam", 148).
?DOC(
    " Compute the CRC-64 (ECMA-182, reflected) checksum of `data` used\n"
    " by the xz block-check field (`check_type = 4`, see RFC `xz-format`\n"
    " ยง2.1.1.2).  Polynomial `0x42F0E1EBA9EA3693` reflected to\n"
    " `0xC96C5795D7870F42`, init / final-xor `0xFFFFFFFFFFFFFFFF`.\n"
    "\n"
    " The result is returned as a `#(low_u32, high_u32)` pair instead of\n"
    " a single 64-bit Int so the implementation stays exact on the\n"
    " JavaScript target, whose `Number` cannot safely represent the full\n"
    " 64-bit space and whose bitwise operators are 32-bit anyway.\n"
).
-spec crc64_xz(bitstring()) -> {integer(), integer()}.
crc64_xz(Data) ->
    {Low, High} = crc64_loop(Data, 4294967295, 4294967295),
    {erlang:'band'(erlang:'bxor'(Low, 4294967295), 4294967295),
        erlang:'band'(erlang:'bxor'(High, 4294967295), 4294967295)}.

-file("src/packkit/checksum.gleam", 230).
?DOC(
    " Mask a CRC-32C value as Snappy's framing layer requires.\n"
    "\n"
    " The masking is `((crc >> 15) | (crc << 17)) + 0xa282ead8` taken\n"
    " modulo `2^32`.\n"
).
-spec snappy_mask(integer()) -> integer().
snappy_mask(Crc) ->
    High = erlang:'bsr'(Crc, 15),
    Low = erlang:'band'(erlang:'bsl'(Crc, 17), 4294967295),
    Rotated = erlang:'bor'(High, Low),
    erlang:'band'(Rotated + 16#A282EAD8, 4294967295).

-file("src/packkit/checksum.gleam", 660).
-spec u32_add(integer(), integer()) -> integer().
u32_add(X, Y) ->
    erlang:'band'(X + Y, 4294967295).

-file("src/packkit/checksum.gleam", 664).
-spec u32_rotr(integer(), integer()) -> integer().
u32_rotr(X, N) ->
    Right = erlang:'bsr'(X, N),
    Left = erlang:'band'(erlang:'bsl'(X, 32 - N), 4294967295),
    erlang:'bor'(Right, Left).

-file("src/packkit/checksum.gleam", 670).
-spec sha256_small_sigma0(integer()) -> integer().
sha256_small_sigma0(X) ->
    erlang:'bxor'(
        erlang:'bxor'(u32_rotr(X, 7), u32_rotr(X, 18)),
        erlang:'bsr'(X, 3)
    ).

-file("src/packkit/checksum.gleam", 677).
-spec sha256_small_sigma1(integer()) -> integer().
sha256_small_sigma1(X) ->
    erlang:'bxor'(
        erlang:'bxor'(u32_rotr(X, 17), u32_rotr(X, 19)),
        erlang:'bsr'(X, 10)
    ).

-file("src/packkit/checksum.gleam", 590).
?DOC(
    " Build W[0..63] in reverse order (newest at head) so the head of the\n"
    " list is `W[count - 1]`.  When `count` reaches 64 we have the full\n"
    " schedule and the caller reverses it.\n"
).
-spec expand_schedule(list(integer()), integer()) -> list(integer()).
expand_schedule(Reversed, Count) ->
    case Count of
        64 ->
            Reversed;

        _ ->
            W_m2 = nth_from_head(Reversed, 1),
            W_m7 = nth_from_head(Reversed, 6),
            W_m15 = nth_from_head(Reversed, 14),
            W_m16 = nth_from_head(Reversed, 15),
            S0 = sha256_small_sigma0(W_m15),
            S1 = sha256_small_sigma1(W_m2),
            Next = u32_add(u32_add(u32_add(W_m16, S0), W_m7), S1),
            expand_schedule([Next | Reversed], Count + 1)
    end.

-file("src/packkit/checksum.gleam", 684).
-spec sha256_big_sigma0(integer()) -> integer().
sha256_big_sigma0(X) ->
    erlang:'bxor'(
        erlang:'bxor'(u32_rotr(X, 2), u32_rotr(X, 13)),
        u32_rotr(X, 22)
    ).

-file("src/packkit/checksum.gleam", 691).
-spec sha256_big_sigma1(integer()) -> integer().
sha256_big_sigma1(X) ->
    erlang:'bxor'(
        erlang:'bxor'(u32_rotr(X, 6), u32_rotr(X, 11)),
        u32_rotr(X, 25)
    ).

-file("src/packkit/checksum.gleam", 614).
-spec sha256_compress(
    list(integer()),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> {integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()}.
sha256_compress(Schedule, Round, A, B, C, D, E, F, G, H) ->
    case Schedule of
        [] ->
            {A, B, C, D, E, F, G, H};

        [W | Rest] ->
            S1 = sha256_big_sigma1(E),
            Ch = erlang:'bxor'(
                erlang:'band'(E, F),
                erlang:'band'(erlang:'bxor'(E, 4294967295), G)
            ),
            Temp1 = u32_add(
                u32_add(u32_add(u32_add(H, S1), Ch), sha256_k(Round)),
                W
            ),
            S0 = sha256_big_sigma0(A),
            Maj = erlang:'bxor'(
                erlang:'bxor'(erlang:'band'(A, B), erlang:'band'(A, C)),
                erlang:'band'(B, C)
            ),
            Temp2 = u32_add(S0, Maj),
            sha256_compress(
                Rest,
                Round + 1,
                u32_add(Temp1, Temp2),
                A,
                B,
                C,
                u32_add(D, Temp1),
                E,
                F,
                G
            )
    end.

-file("src/packkit/checksum.gleam", 507).
-spec sha256_process_blocks(
    bitstring(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> {integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()}.
sha256_process_blocks(Data, H0, H1, H2, H3, H4, H5, H6, H7) ->
    case Data of
        <<>> ->
            {H0, H1, H2, H3, H4, H5, H6, H7};

        <<W0:32/big,
            W1:32/big,
            W2:32/big,
            W3:32/big,
            W4:32/big,
            W5:32/big,
            W6:32/big,
            W7:32/big,
            W8:32/big,
            W9:32/big,
            W10:32/big,
            W11:32/big,
            W12:32/big,
            W13:32/big,
            W14:32/big,
            W15:32/big,
            Rest/binary>> ->
            Initial = [W15,
                W14,
                W13,
                W12,
                W11,
                W10,
                W9,
                W8,
                W7,
                W6,
                W5,
                W4,
                W3,
                W2,
                W1,
                W0],
            Schedule = expand_schedule(Initial, 16),
            {A, B, C, D, E, F, G, H} = sha256_compress(
                lists:reverse(Schedule),
                0,
                H0,
                H1,
                H2,
                H3,
                H4,
                H5,
                H6,
                H7
            ),
            sha256_process_blocks(
                Rest,
                u32_add(H0, A),
                u32_add(H1, B),
                u32_add(H2, C),
                u32_add(H3, D),
                u32_add(H4, E),
                u32_add(H5, F),
                u32_add(H6, G),
                u32_add(H7, H)
            );

        _ ->
            {H0, H1, H2, H3, H4, H5, H6, H7}
    end.

-file("src/packkit/checksum.gleam", 399).
?DOC(
    " Apply FIPS 180-4 padding to whatever bytes are still buffered\n"
    " in `state`, process the final block(s), and return the 32-byte\n"
    " digest.\n"
).
-spec sha256_finalize(sha256_state()) -> bitstring().
sha256_finalize(State) ->
    Bit_len = erlang:element(11, State) * 8,
    Buf_len = erlang:byte_size(erlang:element(10, State)),
    Mod_len = mod(Buf_len + 9, 64),
    Zeros = case Mod_len of
        0 ->
            0;

        N ->
            64 - N
    end,
    Padding = pad_zeros(Zeros, <<>>),
    Final_block = gleam_stdlib:bit_array_concat(
        [erlang:element(10, State), <<16#80>>, Padding, <<Bit_len:64/big>>]
    ),
    {H0, H1, H2, H3, H4, H5, H6, H7} = sha256_process_blocks(
        Final_block,
        erlang:element(2, State),
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        erlang:element(6, State),
        erlang:element(7, State),
        erlang:element(8, State),
        erlang:element(9, State)
    ),
    <<H0:32, H1:32, H2:32, H3:32, H4:32, H5:32, H6:32, H7:32>>.

-file("src/packkit/checksum.gleam", 434).
-spec sha256_consume_blocks(
    bitstring(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> {integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    bitstring()}.
sha256_consume_blocks(Data, H0, H1, H2, H3, H4, H5, H6, H7) ->
    case Data of
        <<Block:64/binary, Rest/binary>> ->
            {A0, A1, A2, A3, A4, A5, A6, A7} = sha256_process_blocks(
                Block,
                H0,
                H1,
                H2,
                H3,
                H4,
                H5,
                H6,
                H7
            ),
            sha256_consume_blocks(Rest, A0, A1, A2, A3, A4, A5, A6, A7);

        _ ->
            {H0, H1, H2, H3, H4, H5, H6, H7, Data}
    end.

-file("src/packkit/checksum.gleam", 367).
?DOC(
    " Absorb `data` into the running SHA-256 state.  Bytes accumulate\n"
    " in an internal buffer and full 64-byte blocks are consumed as\n"
    " soon as they're available, so callers may pass arbitrarily small\n"
    " chunks without blowing memory.\n"
).
-spec sha256_update(sha256_state(), bitstring()) -> sha256_state().
sha256_update(State, Data) ->
    Combined = <<(erlang:element(10, State))/bitstring, Data/bitstring>>,
    New_total = erlang:element(11, State) + erlang:byte_size(Data),
    {H0, H1, H2, H3, H4, H5, H6, H7, Remainder} = sha256_consume_blocks(
        Combined,
        erlang:element(2, State),
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        erlang:element(6, State),
        erlang:element(7, State),
        erlang:element(8, State),
        erlang:element(9, State)
    ),
    {sha256_state, H0, H1, H2, H3, H4, H5, H6, H7, Remainder, New_total}.

-file("src/packkit/checksum.gleam", 458).
?DOC(
    " Compute the SHA-256 digest of `data` and return the 32-byte digest\n"
    " as a `BitArray`.  Used by the xz block-check field (`check_type =\n"
    " 10`).\n"
).
-spec sha256(bitstring()) -> bitstring().
sha256(Data) ->
    Padded = sha256_pad(Data),
    {H0, H1, H2, H3, H4, H5, H6, H7} = sha256_process_blocks(
        Padded,
        16#6A09E667,
        16#BB67AE85,
        16#3C6EF372,
        16#A54FF53A,
        16#510E527F,
        16#9B05688C,
        16#1F83D9AB,
        16#5BE0CD19
    ),
    <<H0:32, H1:32, H2:32, H3:32, H4:32, H5:32, H6:32, H7:32>>.

-file("src/packkit/checksum.gleam", 878).
-spec sha1_round_constants(integer(), integer(), integer(), integer()) -> {integer(),
    integer()}.
sha1_round_constants(Round, B, C, D) ->
    case Round of
        N when N < 20 ->
            {erlang:'bor'(
                    erlang:'band'(B, C),
                    erlang:'band'(erlang:'bxor'(B, 4294967295), D)
                ),
                16#5A827999};

        N@1 when N@1 < 40 ->
            {erlang:'bxor'(erlang:'bxor'(B, C), D), 16#6ED9EBA1};

        N@2 when N@2 < 60 ->
            {erlang:'bor'(
                    erlang:'bor'(erlang:'band'(B, C), erlang:'band'(B, D)),
                    erlang:'band'(C, D)
                ),
                16#8F1BBCDC};

        _ ->
            {erlang:'bxor'(erlang:'bxor'(B, C), D), 16#CA62C1D6}
    end.

-file("src/packkit/checksum.gleam", 905).
-spec u32_rotl(integer(), integer()) -> integer().
u32_rotl(X, N) ->
    Left = erlang:'band'(erlang:'bsl'(X, N), 4294967295),
    Right = erlang:'bsr'(X, 32 - N),
    erlang:'bor'(Left, Right).

-file("src/packkit/checksum.gleam", 841).
-spec sha1_expand_schedule(list(integer()), integer()) -> list(integer()).
sha1_expand_schedule(Reversed, Count) ->
    case Count of
        80 ->
            Reversed;

        _ ->
            W3 = nth_from_head(Reversed, 2),
            W8 = nth_from_head(Reversed, 7),
            W14 = nth_from_head(Reversed, 13),
            W16 = nth_from_head(Reversed, 15),
            Mixed = erlang:'bxor'(
                erlang:'bxor'(W3, W8),
                erlang:'bxor'(W14, W16)
            ),
            sha1_expand_schedule([u32_rotl(Mixed, 1) | Reversed], Count + 1)
    end.

-file("src/packkit/checksum.gleam", 859).
-spec sha1_compress(
    list(integer()),
    integer(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> {integer(), integer(), integer(), integer(), integer()}.
sha1_compress(Schedule, A, B, C, D, E, Round) ->
    case Schedule of
        [] ->
            {A, B, C, D, E};

        [W | Rest] ->
            {F, K} = sha1_round_constants(Round, B, C, D),
            T = u32_add(u32_add(u32_add(u32_add(u32_rotl(A, 5), F), E), K), W),
            sha1_compress(Rest, T, A, u32_rotl(B, 30), C, D, Round + 1)
    end.

-file("src/packkit/checksum.gleam", 791).
-spec sha1_process_blocks(
    bitstring(),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> {integer(), integer(), integer(), integer(), integer()}.
sha1_process_blocks(Data, H0, H1, H2, H3, H4) ->
    case Data of
        <<>> ->
            {H0, H1, H2, H3, H4};

        <<W0:32/big,
            W1:32/big,
            W2:32/big,
            W3:32/big,
            W4:32/big,
            W5:32/big,
            W6:32/big,
            W7:32/big,
            W8:32/big,
            W9:32/big,
            W10:32/big,
            W11:32/big,
            W12:32/big,
            W13:32/big,
            W14:32/big,
            W15:32/big,
            Rest/binary>> ->
            Schedule_reversed = [W15,
                W14,
                W13,
                W12,
                W11,
                W10,
                W9,
                W8,
                W7,
                W6,
                W5,
                W4,
                W3,
                W2,
                W1,
                W0],
            Full_reversed = sha1_expand_schedule(Schedule_reversed, 16),
            Full = lists:reverse(Full_reversed),
            {A, B, C, D, E} = sha1_compress(Full, H0, H1, H2, H3, H4, 0),
            sha1_process_blocks(
                Rest,
                u32_add(H0, A),
                u32_add(H1, B),
                u32_add(H2, C),
                u32_add(H3, D),
                u32_add(H4, E)
            );

        _ ->
            {H0, H1, H2, H3, H4}
    end.

-file("src/packkit/checksum.gleam", 777).
?DOC(
    " Compute the SHA-1 digest of `data` (FIPS 180-4 ยง6.1).  Returns a\n"
    " 20-byte `BitArray`.  Used by `hmac_sha1` for ZIP AE-x.\n"
).
-spec sha1(bitstring()) -> bitstring().
sha1(Data) ->
    Padded = sha256_pad(Data),
    {H0, H1, H2, H3, H4} = sha1_process_blocks(
        Padded,
        16#67452301,
        16#EFCDAB89,
        16#98BADCFE,
        16#10325476,
        16#C3D2E1F0
    ),
    <<H0:32, H1:32, H2:32, H3:32, H4:32>>.

-file("src/packkit/checksum.gleam", 930).
-spec hmac_sha1_prepare_key(bitstring()) -> bitstring().
hmac_sha1_prepare_key(Key) ->
    Key_size = erlang:byte_size(Key),
    Normalised = case Key_size > 64 of
        true ->
            sha1(Key);

        false ->
            Key
    end,
    Normalised_size = erlang:byte_size(Normalised),
    Padding_len = 64 - Normalised_size,
    case Padding_len of
        0 ->
            Normalised;

        _ ->
            gleam_stdlib:bit_array_concat(
                [Normalised, pad_zeros(Padding_len, <<>>)]
            )
    end.

-file("src/packkit/checksum.gleam", 922).
?DOC(
    " Compute the HMAC-SHA1 of `data` keyed by `key` (RFC 2104).\n"
    " Returns the 20-byte tag as a `BitArray`.\n"
).
-spec hmac_sha1(bitstring(), bitstring()) -> bitstring().
hmac_sha1(Key, Data) ->
    Key_block = hmac_sha1_prepare_key(Key),
    Outer_pad = xor_with_byte(Key_block, 16#5C),
    Inner_pad = xor_with_byte(Key_block, 16#36),
    Inner = sha1(gleam_stdlib:bit_array_concat([Inner_pad, Data])),
    sha1(gleam_stdlib:bit_array_concat([Outer_pad, Inner])).

-file("src/packkit/checksum.gleam", 1018).
-spec pbkdf2_iterate(bitstring(), bitstring(), bitstring(), integer()) -> bitstring().
pbkdf2_iterate(Password, Previous, Accumulator, Remaining) ->
    case Remaining of
        0 ->
            Accumulator;

        _ ->
            Next = hmac_sha1(Password, Previous),
            pbkdf2_iterate(
                Password,
                Next,
                xor_bit_arrays(Accumulator, Next),
                Remaining - 1
            )
    end.

-file("src/packkit/checksum.gleam", 1004).
-spec pbkdf2_one_block(bitstring(), bitstring(), integer(), integer()) -> bitstring().
pbkdf2_one_block(Password, Salt, Iterations, Index) ->
    Initial = hmac_sha1(
        Password,
        gleam_stdlib:bit_array_concat([Salt, <<Index:32/big>>])
    ),
    pbkdf2_iterate(Password, Initial, Initial, Iterations - 1).

-file("src/packkit/checksum.gleam", 984).
-spec pbkdf2_blocks(
    bitstring(),
    bitstring(),
    integer(),
    integer(),
    integer(),
    bitstring()
) -> bitstring().
pbkdf2_blocks(Password, Salt, Iterations, Block_count, Index, Acc) ->
    gleam@bool:guard(
        Index > Block_count,
        Acc,
        fun() ->
            Block = pbkdf2_one_block(Password, Salt, Iterations, Index),
            pbkdf2_blocks(
                Password,
                Salt,
                Iterations,
                Block_count,
                Index + 1,
                gleam_stdlib:bit_array_concat([Acc, Block])
            )
        end
    ).

-file("src/packkit/checksum.gleam", 969).
?DOC(
    " Derive `dk_len` bytes from `password` and `salt` via PBKDF2-HMAC-SHA1\n"
    " (RFC 2898).  Used by the ZIP AE-x scheme with `iterations = 1000`\n"
    " and `dk_len = key_len * 2 + 2` (encryption key + HMAC key +\n"
    " 2-byte password verifier).\n"
).
-spec pbkdf2_hmac_sha1(bitstring(), bitstring(), integer(), integer()) -> bitstring().
pbkdf2_hmac_sha1(Password, Salt, Iterations, Dk_len) ->
    Block_count = (Dk_len + 19) div 20,
    Raw = pbkdf2_blocks(Password, Salt, Iterations, Block_count, 1, <<>>),
    gleam@result:unwrap(gleam_stdlib:bit_array_slice(Raw, 0, Dk_len), Raw).