Packages

Implements dependency free CRC64 checksum calculation, great for validating Amazon AWS S3 downloads

Current section

Files

Jump to
crc_64 lib crc64.ex
Raw

lib/crc64.ex

defmodule Crc64 do
@moduledoc File.read!("README.md")
import Bitwise
@poly 0x9A6C9329AC4BC9B5
@init 0xFFFFFFFFFFFFFFFF
@xor_out 0xFFFFFFFFFFFFFFFF
@doc """
Calculates the CRC64 checksum for the given binary data.
Returns the checksum as an integer value.
## Examples
iex> Crc64.calculate("123456789")
0xAE8B14860A799888
"""
@spec calculate(binary()) :: non_neg_integer()
def calculate(data) when is_binary(data) do
data
|> :binary.bin_to_list()
|> Enum.reduce(@init, &update_crc(&1, &2))
|> finalize()
end
@doc """
Calculates the CRC64 checksum for the given binary data and returns it as a Base64 encoded string.
This format is compatible with the AWS S3 CRC64 checksums.
## Examples
iex> Crc64.calculate_base64("Hello World!")
"AuUcyF784aU="
"""
@spec calculate_base64(binary()) :: String.t()
def calculate_base64(data) when is_binary(data) do
data
|> calculate()
|> to_base64()
end
@doc """
Validates if the given checksum matches the calculated checksum for the data.
Returns `true` if the checksum is valid, `false` otherwise.
## Examples
iex> Crc64.valid_checksum?("Hello World!", "AuUcyF784aU=")
true
iex> Crc64.valid_checksum?("Hello World!", "u5WU6IUEP4mKJw==")
false
"""
@spec valid_checksum?(binary(), String.t()) :: boolean()
def valid_checksum?(data, checksum) when is_binary(data) and is_binary(checksum) do
calculate_base64(data) === checksum
end
@doc """
Validates if the given checksum matches the calculated checksum for the file contents.
Returns `true` if the checksum is valid, `false` otherwise.
Raises an error if the file does not exist or cannot be read.
## Examples
iex> File.write!("test.txt", "Hello World!")
:ok
iex> Crc64.valid_file_checksum?("test.txt", "AuUcyF784aU=")
true
iex> File.rm!("test.txt")
:ok
"""
@spec valid_file_checksum?(String.t(), String.t()) :: boolean()
def valid_file_checksum?(file_path, checksum) when is_binary(file_path) and is_binary(checksum) do
file_path
|> File.read!()
|> valid_checksum?(checksum)
end
@spec update_crc(byte(), non_neg_integer()) :: non_neg_integer()
defp update_crc(byte, crc) do
crc = bxor(crc, byte)
Enum.reduce(1..8, crc, fn _, acc ->
if (acc &&& 1) == 1 do
bxor((acc >>> 1), @poly)
else
acc >>> 1
end
end)
end
@spec finalize(non_neg_integer()) :: non_neg_integer()
defp finalize(crc), do: bxor(crc, @xor_out)
@spec to_base64(non_neg_integer()) :: String.t()
defp to_base64(crc) do
crc
|> :binary.encode_unsigned(:big)
|> Base.encode64()
end
end