DoAuth.Crypto (do_auth v0.5.0-pre)

Wrappers around just enough of enacl to be able to exectute both client and server parts of the protocol.

Link to this section Summary

Types

Only accept atoms as keys of canonicalisable entities.

Only accept atoms, strings and numbers as values of canonocalisable entities.

Detached signature along with the public key needed to validate.

Enacl iolist.

Message representation: either enacl text or urlsafe encoding of some binary.

Enacl text data representation.

Hash sizes, analogously.

This is a keypair, marking explicitly what's private (secret) key and what's public key.

A keypair that maybe has its secret component omitted.

Derivation limits type. Currently used in default_params function and slip type. They govern how many instructions and how much memory is allowed to be consumed by the system.

Marked public key.

Libsodium-compatible salt size.

Marked private (secret) key.

Main key derivation slip. Returned by main_key_init, and required for main_key_reproduce.

Functions

Unkeyed generic hash of an iolist, represented as a URL-safe Base64 string. Note: the hash size is crypto_generic_BYTES, manually written down as @hash_size macro in this file.

Preventing canonicalisation bugs by ordering maps lexicographically into a list. NB! This makes it so that list representations of JSON objects are also accepted by verifiers, but it's OK, since no data can seemingly be falsified like this.

Currently we use moderate limits, because we want to support small computers. TODO: Use configurable values here, based on the power of a computer.

Create a signing keypair from main key at index n.

Generate slip and main key from password.

Generate slip and main key from password with given parameters. This function is used directly for testing and flexibility, but shouldn't be normally used. For most purposes, you should use main_key_init/1.

Generate main key from password and a slip.

DON'T USE THIS FUNCTION, IT'S EXPORTED JUST FOR BETTER TYPECHEKING!

Keyed (salted) generic hash of an iolist, represented as a URL-safe Base64 string. The key is obtained from the application configuration's paramterer "hash_salt". Note: this parameter is expected to be long-lived and secret. Note: the hash size is crypto_generic_BYTES, manually written down as @hash_size macro in this file.

Simple way to get the server keypair.

DON'T USE THIS FUNCTION, IT'S EXPORTED JUST FOR BETTER TYPECHEKING!

Wrapper around detached signatures that creates an object tracking corresponding public key.

Signs a map and embeds detached signature into it. This function is configurable via options and has the following options

Verify a detached signature object.

Verifies a map that has a proof embedded into it by converting said object into a map, deleting embedding, canonicalising the result and verifying the result against the embedding. This function is configurable via options and has the following options

Verify a raw detached signature object.

Link to this section Types

Link to this type

canonicalisable_key()

Specs

canonicalisable_key() :: atom()

Only accept atoms as keys of canonicalisable entities.

Link to this type

canonicalisable_value()

Specs

canonicalisable_value() ::
  atom()
  | Uptight.Base.t()
  | Uptight.Text.t()
  | number()
  | DateTime.t()
  | [canonicalisable_value()]
  | %{required(canonicalisable_key()) => canonicalisable_value()}

Only accept atoms, strings and numbers as values of canonocalisable entities.

Link to this type

canonicalised_value()

Specs

canonicalised_value() ::
  Uptight.Base.t()
  | Uptight.Text.t()
  | number()
  | [[Uptight.Text.t() | canonicalised_value()]]
Link to this type

detached_sig()

Specs

detached_sig() :: %{public: Uptight.Binary.t(), signature: Uptight.Binary.t()}

Detached signature along with the public key needed to validate.

Link to this type

enacl_iolist()

Specs

enacl_iolist() ::
  binary()
  | maybe_improper_list(
      binary() | maybe_improper_list(any(), binary() | []) | byte(),
      binary() | []
    )

Enacl iolist.

Link to this type

enacl_message()

Specs

enacl_message() :: enacl_text() | Uptight.Base.Urlsafe.t()

Message representation: either enacl text or urlsafe encoding of some binary.

Link to this type

enacl_text()

Specs

enacl_text() :: Uptight.Text.t() | enacl_iolist()

Enacl text data representation.

Specs

hash() :: Uptight.Binary.t()

Specs

hash_size() :: pos_integer()

Hash sizes, analogously.

Specs

keypair() :: %{secret: Uptight.Binary.t(), public: Uptight.Binary.t()}

This is a keypair, marking explicitly what's private (secret) key and what's public key.

Link to this type

keypair_opt()

Specs

keypair_opt() :: %{
  optional(:secret) => Uptight.Binary.t(),
  public: Uptight.Binary.t()
}

A keypair that maybe has its secret component omitted.

Specs

limits() :: %{ops: :enacl.pwhash_limit(), mem: :enacl.pwhash_limit()}

Derivation limits type. Currently used in default_params function and slip type. They govern how many instructions and how much memory is allowed to be consumed by the system.

Specs

params() :: %{
  ops: :enacl.pwhash_limit(),
  mem: :enacl.pwhash_limit(),
  salt_size: salt_size()
}

Specs

public() :: %{public: Uptight.Binary.t()}

Marked public key.

Specs

salt() :: Uptight.Binary.t()

Specs

salt_size() :: pos_integer()

Libsodium-compatible salt size.

Used in defaultparams, but NOT used in @type slip! If you change this parameter, you MUST change :salt, << :: ...>> section of @type slip as well! Some additional safety is provided by defining a simple macro @salt_size below.

Specs

secret() :: %{secret: Uptight.Binary.t()}

Marked private (secret) key.

Specs

slip() :: %{
  ops: :enacl.pwhash_limit(),
  mem: :enacl.pwhash_limit(),
  salt: salt()
}

Main key derivation slip. Returned by main_key_init, and required for main_key_reproduce.

Specs

slip_raw() :: %{
  ops: :enacl.pwhash_limit(),
  mem: :enacl.pwhash_limit(),
  salt: binary()
}

Link to this section Functions

Specs

Link to this function

binary_server_keypair()

Specs

binary_server_keypair() :: %{
  secret: Uptight.Binary.t(),
  public: Uptight.Binary.t()
}
Link to this function

bland_hash(msg)

Specs

bland_hash(enacl_text()) :: String.t()

Unkeyed generic hash of an iolist, represented as a URL-safe Base64 string. Note: the hash size is crypto_generic_BYTES, manually written down as @hash_size macro in this file.

Link to this function

canonical_sign(canonical_term, kp)

Specs

canonical_sign(canonicalised_value(), keypair()) :: Uptight.Result.t()
Link to this function

canonical_sign!(canonical_term, kp)

Specs

canonical_sign!(canonicalised_value(), keypair()) :: detached_sig()
Link to this function

canonicalise_term(x)

Specs

canonicalise_term(canonicalisable_value()) :: Uptight.Result.t()
Link to this function

canonicalise_term!(v)

Specs

canonicalise_term!(canonicalisable_value()) :: canonicalised_value()

Preventing canonicalisation bugs by ordering maps lexicographically into a list. NB! This makes it so that list representations of JSON objects are also accepted by verifiers, but it's OK, since no data can seemingly be falsified like this.

TODO: Audit this function really well, both here and in JavaScript reference implementation, since a bug here can sabotage the security guarantees of the cryptographic system.

Link to this function

default_params()

Specs

default_params() :: params()

Currently we use moderate limits, because we want to support small computers. TODO: Use configurable values here, based on the power of a computer.

Link to this function

derive_signing_keypair(mkey, n)

Specs

derive_signing_keypair(Uptight.Binary.t(), pos_integer()) :: keypair()

Create a signing keypair from main key at index n.

Link to this function

is_canonicalised?(x)

Specs

is_canonicalised?(any()) :: boolean()
Link to this function

main_key_init(pass)

Specs

main_key_init(enacl_text()) :: {Uptight.Binary.t(), slip()}

Generate slip and main key from password.

Link to this function

main_key_init(pass, map)

Specs

main_key_init(enacl_text(), params()) :: {Uptight.Binary.t(), slip()}

Generate slip and main key from password with given parameters. This function is used directly for testing and flexibility, but shouldn't be normally used. For most purposes, you should use main_key_init/1.

NB! I've used autogenerated spec here, because for some reason, after I moved to enacl types, local typedefs for slip and params stopped working. Gah.

Link to this function

main_key_reproduce(pass, slip)

Specs

main_key_reproduce(enacl_text(), slip()) :: Uptight.Binary.t()

Generate main key from password and a slip.

Link to this function

main_key_reproduce_raw(pass, map)

Specs

main_key_reproduce_raw(binary() | enacl_iolist(), slip_raw()) ::
  Uptight.Binary.t()

Specs

DON'T USE THIS FUNCTION, IT'S EXPORTED JUST FOR BETTER TYPECHEKING!

Link to this function

salted_hash(msg)

Specs

salted_hash(enacl_text()) :: String.t()

Keyed (salted) generic hash of an iolist, represented as a URL-safe Base64 string. The key is obtained from the application configuration's paramterer "hash_salt". Note: this parameter is expected to be long-lived and secret. Note: the hash size is crypto_generic_BYTES, manually written down as @hash_size macro in this file.

Link to this function

server_keypair()

Specs

server_keypair() :: keypair()

Simple way to get the server keypair.

TODO: audit key management practices in Phoenix and here.

Link to this function

server_keypair64()

Specs

server_keypair64() :: keypair()

DON'T USE THIS FUNCTION, IT'S EXPORTED JUST FOR BETTER TYPECHEKING!

Specs

server_pk() :: Uptight.Binary.t()
Link to this function

sig64_to_proof_map(arg1, arg2, timestamp \\ nil)

Specs

sig64_to_proof_map(String.t(), String.t(), DateTime.t() | nil) :: map()

See #26!

Specs

Wrapper around detached signatures that creates an object tracking corresponding public key.

Link to this function

sign_map(kp, the_map, overrides \\ [], defaults \\ sign_map_def_opts())

Specs

sign_map(keypair_opt(), map(), list(), list()) :: Uptight.Result.t()

Signs a map and embeds detached signature into it. This function is configurable via options and has the following options:

  • :proof_field - which field carries the embedded proof. Defaults to "proof".
  • :signature_field - which field of the proof carries the detached signature. Defaults to "signature".
  • :signature - if present, this function won't use :secret from keypair, but instead will add this signature verbatim. No verification shall be conducted in case the signature provided is invalid!
  • :key_field - which field of the proof stores information related to key retrieval. Defaults to "verificationMethod".
  • :key_field_constructor - a function that takes the public key and options and constructs value for :key_field, perhaps stateful. By default it queries for a DID corresponding to the key and returns its string representation.
  • :if_did_missing - if set to :insert, default key constructor will insert a new DID, otherwise will error out. By default set to :fail.
  • ignore: [] - list of fields to omit from building a canonicalised object. Defaults to ["id"].
Link to this function

sign_map!(kp, to_prove, overrides \\ [], defopts \\ sign_map_def_opts())

Specs

sign_map!(keypair_opt(), map(), list(), list()) :: map()
Link to this function

sign_map_def_opts()

Specs

sign_map_def_opts() :: [
  {:key_field_constructor, (any(), any() -> any())}
  | {:key_field, <<_::144>>}
  | {:proof_field, <<_::40>>}
  | {:signature_field, <<_::72>>},
  ...
]
Link to this function

sign_raw(msg, map)

Specs

sign_raw(
  enacl_message(),
  %{:public => any(), :secret => binary(), optional(any()) => any()}
) :: any()
Link to this function

urlsafe_server_keypair()

Specs

urlsafe_server_keypair() :: %{
  secret: Uptight.Base.Urlsafe.t(),
  public: Uptight.Base.Urlsafe.t()
}
Link to this function

urlsafe_server_pk()

Specs

urlsafe_server_pk() :: Uptight.Base.Urlsafe.t()
Link to this function

verify(msg, map)

Specs

verify(enacl_message(), detached_sig()) :: boolean()

Verify a detached signature object.

Link to this function

verify_map( verifiable_map, overrides \\ [], defaults \\ [ proof_field: "proof", signature_field: "signature", key_extractor: fn proof_map -> Map.get( proof_map, "verificationMethod" ) end, ignore: ["id"] ] )

Specs

verify_map(map(), list(), list()) :: Uptight.Result.t()

Verifies a map that has a proof embedded into it by converting said object into a map, deleting embedding, canonicalising the result and verifying the result against the embedding. This function is configurable via options and has the following options:

  • :proof_field - which field carries the embedded proof. Defaults to "proof".
  • ignore: [] - list of fields to ignore. Defaults to ["id"].
  • :signature_field - which field carries the detached signature. Defaults to "signature".
  • :key_extractor - a function that retreives public key needed to verify embedded proof. Defaults to taking 0th element of DID.all_by_string ran against "verificationMethod" field of the proof object. As per https://www.w3.org/TR/vc-data-model/, proof object may be a list, this function accounts for it. Uses Uptight.Result for returning a value. Exceptions are wrapped in Err and aren't re-raised.
Link to this function

verify_raw(msg, map)

Specs

verify_raw(
  binary()
  | maybe_improper_list()
  | %{
      :__struct__ => Uptight.Base.Urlsafe | Uptight.Text,
      optional(any()) => any()
    },
  %{:public => binary(), :signature => binary(), optional(any()) => any()}
) :: boolean()

Verify a raw detached signature object.