# `Tspex.TimeStampedData`
[🔗](https://github.com/openspb/tspex/blob/v0.1.0/lib/tspex/timestamped_data.ex#L1)

A document bound to a chain of time-stamps: `TimeStampedData`, RFC 5544.

**Experimental.** RFC 5544 does not say which octets the second and later
time-stamps cover, and there is no tool to test against; this module
makes a choice and documents it below. The functions and fields here may
change in a minor release until the choice is confirmed by another
implementation. For the same reason only `decode!/2` has a `!` variant for
now: the other functions return `{:ok, value}` or `{:error, %Tspex.Error{}}`.

A `TimeStampedData` is a `ContentInfo` (content type
`id-ct-timestampedData`) that holds the document, or says where to find
it (`dataUri`); some metadata (`fileName`, `mediaType` and attributes);
and the temporal evidence, which here is a list of time-stamp tokens. Each
token covers the one before it, so a long chain shows the document existed
at the time of the first token, and that each token existed when the next
one was issued.

    {:ok, imprint} = Tspex.TimeStampedData.imprint(contract, file_name: "contract.pdf")
    {:ok, first} = Tspex.stamp(imprint, url: url, trust: roots)
    {:ok, tsd} = Tspex.TimeStampedData.new(contract, first, file_name: "contract.pdf")
    File.write!("contract.pdf.tsd", Tspex.TimeStampedData.encode(tsd))

    {:ok, result} = Tspex.TimeStampedData.verify(File.read!("contract.pdf.tsd"), trust: roots)
    result.time

## What each token covers

  * The first token: the value octets of `content` (or of the document
    given as `:content`, since `dataUri` is never fetched). When the
    metadata has `hashProtected` true, the **DER encoding of the
    `MetaData`, followed by the content** (RFC 5544, section 2).
  * Each later token: **the previous element of the sequence**, as the
    `TimeStampAndCRL` that is in the structure, byte for byte. This is
    the reading of "computed over its preceding element"; it includes the
    `crl` of that element if it has one. The elements this module writes
    are DER when the tokens are.
  * The `crl` of an element shows that the certificate of the TSA of that
    element's token was not revoked when the next element was added, so
    it is put in the element before the next token is asked for: see
    `extension_imprint/2`.

## What it does not do

  * It never fetches `dataUri`. Give the document as `:content`.
  * `ersEvidence` and `otherEvidence` are read and written as bytes, and
    `verify/2` answers `:unsupported_evidence` for them.
  * It does not check the `crl` of an element. `verify/2` returns it, with
    the stamps, for you to check (the guide on verification shows how, with
    the recipe for revocation); `:revocation` is called for each token like
    in `Tspex.verify/3`.
  * It does not refuse a chain whose `genTime` goes back unless you give
    `:max_time_back`; `:ordered` in the result says whether it did.
  * The token of a time-stamp is only as good as its TSA, as always: read
    `Tspex.verify/3` for what the checks of each token mean.

# `element`

```elixir
@type element() :: %{token: binary(), crl: binary() | nil, raw: binary()}
```

A `TimeStampAndCRL`: the token in DER, the CRL in DER or `nil`, and `raw`,
the whole element as it is in the structure, which the next token covers.

# `evidence`

```elixir
@type evidence() ::
  {:tst, [element(), ...]} | {:ers, binary()} | {:other, tuple(), binary()}
```

The temporal evidence: a chain of time-stamps (`:tst`), the evidence of an
archive time-stamp (`:ers`) or another kind, by its OID. Only the first is
verified.

# `meta_data`

```elixir
@type meta_data() :: %{
  hash_protected: boolean(),
  file_name: String.t() | nil,
  media_type: String.t() | nil,
  other: [%{oid: tuple(), values: [binary()]}]
}
```

The `MetaData`: `other` is a list of attributes, `%{oid: tuple, values: [der]}`.

# `t`

```elixir
@type t() :: %Tspex.TimeStampedData{
  content: binary() | nil,
  data_uri: String.t() | nil,
  evidence: evidence(),
  meta_data: meta_data() | nil,
  version: 1
}
```

A `TimeStampedData` (RFC 5544), experimental.

# `decode`

```elixir
@spec decode(binary(), keyword()) :: {:ok, t()} | {:error, Tspex.Error.t()}
```

Reads a `TimeStampedData` from a `ContentInfo` in BER or DER, without
verifying it. Takes the limits `:max_size`, `:max_depth`, `:max_nodes` and
`:max_stamps` (64 elements in the chain).

Errors: `:malformed`, `:limit_exceeded`, `:unsupported_version`.

# `decode!`

```elixir
@spec decode!(binary(), keyword()) :: t()
```

Like `decode/2`, but raises `Tspex.Error`.

# `encode`

```elixir
@spec encode(t()) :: binary()
```

The `ContentInfo` of `tsd` in DER. The elements of the chain are written
as they are, so what the tokens cover does not change.

# `extend`

```elixir
@spec extend(t(), Tspex.Verified.t() | binary(), keyword()) ::
  {:ok, t()} | {:error, Tspex.Error.t()}
```

Adds a time-stamp to the chain of `tsd`. `stamp` is as in `new/3`, and has
to cover the last element of the chain, with the `:crl` if one is given
(see `extension_imprint/2`).

Takes `:crl` and, for a token in DER, the options of `Tspex.verify/3`.
Only a chain of `tstEvidence` can be extended.

# `extension_imprint`

```elixir
@spec extension_imprint(t(), keyword()) ::
  {:ok, {:digest, Tspex.hash(), binary()}} | {:error, Tspex.Error.t()}
```

The imprint to time-stamp next, to extend the chain of `tsd`: the hash of
the last element, with the `crl` that is to go in it.

Options: `:crl` (a CertificateList in DER, which replaces the one in the
last element, if any), `:hash` (`:sha256`) and `:allow_sha1`. Ask the TSA
for a token over this imprint, then give `extend/3` the same `:crl`.

# `imprint`

```elixir
@spec imprint(Tspex.data(), keyword()) :: {:ok, {:digest, Tspex.hash(), binary()}}
```

The imprint to time-stamp first for `content`: the hash of the content, or
of the `MetaData` and the content when `hash_protected: true`. Returns
`{:ok, {:digest, hash, digest}}`, which `Tspex.request/2`, `Tspex.stamp/2`
and `new/3` take.

`content` is a binary or `{:stream, enumerable}` (read once).

Options:

  * `:hash` - `:sha256` (the default), `:sha224`, `:sha384`, `:sha512`;
    `:sha1` only with `allow_sha1: true`.
  * `:file_name`, `:media_type`, `:other_meta_data` - the `MetaData`. The
    first is a UTF-8 string, the second ASCII, the third a list of
    attributes `%{oid: tuple, values: [der]}`. At least one is needed for
    there to be a `MetaData`.
  * `:hash_protected` - `true` puts the `MetaData` under the first token
    (`false`, the default). It needs a `MetaData`.
  * `:allow_sha1` - `false`.

With `hash_protected: true` the imprint covers the file name too, so it is
not the hash of the content alone:

    iex> alias Tspex.TimeStampedData
    iex> {:ok, {:digest, :sha256, plain}} = TimeStampedData.imprint("hello")
    iex> Base.encode16(plain, case: :lower)
    "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
    iex> {:ok, bound} = TimeStampedData.imprint("hello", file_name: "a.txt", hash_protected: true)
    iex> bound == {:digest, :sha256, plain}
    false

# `new`

```elixir
@spec new(Tspex.data(), Tspex.Verified.t() | binary(), keyword()) ::
  {:ok, t()} | {:error, Tspex.Error.t()}
```

Makes a `TimeStampedData` of `content` and the time-stamp of its imprint
(see `imprint/2`).

`stamp` is a `t:Tspex.Verified.t/0`, taken to have been verified already,
or a token in DER, which is verified first with the options of
`Tspex.verify/3` (`:trust` is required). Either way its imprint has to be
the one of `content` and the `MetaData` (`:imprint_mismatch` otherwise).

Takes the options of `imprint/2` except `:hash` and `:allow_sha1` (the hash
is the token's) and:

  * `:data_uri` - where the document can be found, a URI as ASCII text.
  * `:detached` - `true` leaves the content out of the structure, which
    then needs a `:data_uri`. A stream is always detached.

# `time_stamps`

```elixir
@spec time_stamps(t() | binary(), keyword()) ::
  {:ok, [Tspex.TSTInfo.t()]} | {:error, Tspex.Error.t()}
```

The `TSTInfo` of each token of the chain, read and not verified, in the
order of the chain. Takes the limits of `decode/2`. A chain of other
evidence is `:unsupported_evidence`.

# `verify`

```elixir
@spec verify(t() | binary(), keyword()) :: {:ok, map()} | {:error, Tspex.Error.t()}
```

Verifies a `TimeStampedData`, given as DER or as the struct: the content
and the whole chain.

Each token is verified like `Tspex.verify/3` verifies a stored token, with
the same options, against what it has to cover (see the module doc): the
first against the content (and the `MetaData` if it is hash protected),
the others against the element before them. The certificate of each TSA
is judged at the `genTime` of its own token.

Options: those of `Tspex.verify/3` (`:trust` is required), the limits of
`decode/2`, and:

  * `:content` - the document, as a binary or `{:stream, enumerable}`, for
    a structure that does not carry it. A structure that carries its
    content and is given `:content` is `:unexpected_content`, and one with
    neither is `:content_missing`. The `dataUri` is not used.
  * `:max_time_back` - how many seconds the `genTime` of a token may be
    earlier than the one of the token before it. A token covers the
    element that holds the one before, so it was issued later; if its
    `genTime` is earlier, one of the two clocks is wrong, or lies. With a
    number, a token earlier by more than that is `:time_order`; `0` asks
    for a strictly non-decreasing chain. `nil` (the default) does not
    refuse: the result says it in `:ordered`. TSAs that are not the same
    have clocks that differ by seconds, so choose the number knowing which.

Returns `{:ok, result}` with:

  * `:time` - the `genTime` of the first token, when the document existed.
  * `:stamps` - the `t:Tspex.Verified.t/0` of each token, in order.
  * `:ordered` - `true` when no token has a `genTime` earlier than the
    one before it (see `:max_time_back`).
  * `:crls` - the `crl` of each element in DER, or `nil`. Not checked here;
    the guide on verification shows how to check them with the recipe for
    revocation.
  * `:meta_data` - the `MetaData`, or `nil`.

Evidence that is not `tstEvidence` is `:unsupported_evidence`.

---

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