# `Ankusa.BlobStore.Checksum`
[🔗](https://github.com/jamescarr/ankusa/blob/ankusa-v0.7.0/packages/ankusa/lib/ankusa/blob_store/checksum.ex#L1)

The integrity headers the HTTP object stores take on a `PUT`, and the check of
the digest they send back on a `GET`.

| Adapter | On `PUT` | Verified on `GET` |
| --- | --- | --- |
| `S3` | `x-amz-checksum-sha256` (and `Content-MD5` on each multipart part) | `x-amz-checksum-sha256`, requested with `x-amz-checksum-mode: ENABLED` |
| `GCS` | `x-goog-hash: md5=…` | `x-goog-hash` |
| `Azure` | `Content-MD5` and `x-ms-blob-content-md5` | `Content-MD5` |
| `OCI` | `Content-MD5` (and `x-content-sha256`, which the signature covers) | `opc-content-md5` |

The store validates the digest it was sent, so bytes damaged on the way up are
refused with a `4xx`; a digest the store sends back is compared with the bytes
received, so bytes damaged on the way down are `{:error, :integrity}`. A
response without the header (the store never kept one, a composite multipart
digest, an emulator) is not checked: absence is not corruption.

# `check_response`

```elixir
@spec check_response(
  %{required(String.t()) =&gt; [String.t()]},
  binary(),
  :sha256 | :md5,
  [String.t()]
) ::
  {:ok, binary()} | {:error, :integrity}
```

Check a `GET` response's `body` against the digest header `names` the store
puts on it (the first of `names` that is present); `:sha256` or `:md5`.
`{:ok, body}` or `{:error, :integrity}`.

# `first`

```elixir
@spec first(%{required(String.t()) =&gt; [String.t()]}, String.t()) :: String.t() | nil
```

The first value of response header `name`, or `nil`.

# `goog_md5`

```elixir
@spec goog_md5(%{required(String.t()) =&gt; [String.t()]}) :: String.t() | nil
```

The md5 a `x-goog-hash` header (`crc32c=…,md5=…`, possibly several headers) carries.

# `md5_base64`

```elixir
@spec md5_base64(iodata()) :: String.t()
```

Base64 MD5 of `data`.

# `sha256_base64`

```elixir
@spec sha256_base64(iodata()) :: String.t()
```

Base64 SHA-256 of `data`.

# `verify`

```elixir
@spec verify(:sha256 | :md5, iodata(), String.t() | nil) :: :ok | {:error, :integrity}
```

`:ok` unless `expected` (the base64 digest a response header carried) is not
the `algorithm` (`:sha256` or `:md5`) digest of `body`. A `nil` or empty
`expected` is `:ok`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
