# `Aker.LicenceFile`
[🔗](https://github.com/udin-io/aker/blob/v0.1.0/lib/aker/licence_file.ex#L1)

Reads a signed licence file and checks every signature and field in it.

The file is one JSON object with four base64 fields:

  * `licence`: the licence JSON bytes, signed by the issuer's working key;
  * `signature`: that signature, 64 bytes of Ed25519;
  * `certificate`: the working key's certificate JSON bytes;
  * `certificate_signature`: the root key's signature of the certificate.

`verify/2` checks the signature over the exact bytes it received before
it parses them, and never re-encodes the JSON. Each refusal is its own
atom and its own log line.

The checks run in this order, and the first failure answers:

  1. `:bad_envelope`: the file is over 64 KB, is not the four-field
     object, or a field has the wrong length or is not base64. Sizes are
     checked before anything is decoded.
  2. `:unknown_root`: no root key in `:root_keys` signed the
     certificate. An empty list refuses every file here.
  3. `:bad_certificate`: the certificate's fields are malformed, or it
     is not for a licence working key.
  4. `:bad_signature`: the certificate's key did not sign the licence.
  5. `:bad_licence`: the licence's fields are malformed.
  6. `:wrong_working_key`: the licence names another working key id.
  7. `:certificate_out_of_date`: the certificate was not in date when
     the licence was issued, or is not now.
  8. `:wrong_product`: the licence is for another product. One root key
     can sign for several products, so this check keeps one product's
     licence off another.
  9. `:wrong_machine`: the licence names another machine id.
 10. `:wrong_install_key`: the licence names another install key, or
     the install key's own error (`:install_key_missing`,
     `:install_key_unreadable`, `:install_key_timeout`) when this install
     has none to compare.
 11. `:install_key_proof_failed`: this install's key did not sign a
     fresh random value that verifies against the licence's key.
 12. `:clock_behind`: the clock reads more than a day before the latest
     time the app has seen, so setting it back cannot extend a term.
 13. `:term_not_started` or `:term_ended`.
 14. `:older_serial`: the licence is older than the one already stored.

# `refusal`

```elixir
@type refusal() ::
  :bad_envelope
  | :unknown_root
  | :bad_certificate
  | :bad_signature
  | :bad_licence
  | :wrong_working_key
  | :certificate_out_of_date
  | :wrong_product
  | :wrong_machine
  | :wrong_install_key
  | :install_key_missing
  | :install_key_unreadable
  | :install_key_timeout
  | :install_key_proof_failed
  | :clock_behind
  | :term_not_started
  | :term_ended
  | :older_serial
```

# `certificate_purpose`

```elixir
@spec certificate_purpose() :: String.t()
```

What a working key's certificate says it is for. A root key's signature
on any other JSON never passes as a certificate.

# `encode`

```elixir
@spec encode(%{
  licence: binary(),
  signature: binary(),
  certificate: binary(),
  certificate_signature: binary()
}) :: binary()
```

The four parts as the file's bytes. `Aker.Issuer` writes a
licence through this, so the issuer and the reader share one shape.

# `features?`

```elixir
@spec features?(term()) :: boolean()
```

Whether `features` is a list a licence may carry: at most 32 names, each
1 to 32 bytes of `a-z`, `0-9`, `_`, `.` and `-`.

# `licence_fits?`

```elixir
@spec licence_fits?(binary()) :: boolean()
```

Whether licence JSON `bytes` fit the size this module reads.

# `max_bytes`

```elixir
@spec max_bytes() :: pos_integer()
```

The largest licence file `verify/2` reads, in bytes.

# `verify`

```elixir
@spec verify(binary(), keyword()) :: {:ok, Aker.Terms.t()} | {:error, refusal()}
```

The licence in `bytes` as `Aker.Terms`, when every check
passes for this install.

Options:

  * `:product`: the product this app is (required);
  * `:root_keys`: the trusted Ed25519 root public keys, raw 32-byte
    binaries (required; see `Aker.PublicKeys.decode/1`);
  * `:machine_id_hash`: this host's machine id hash (required);
  * `:install_key`: `{module, opts}` of this install's key (required);
  * `:now`: the time to check against (default now);
  * `:last_seen`: the latest time the app has seen, or nil;
  * `:stored_serial`: the serial of the licence already stored, or nil.

---

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