Documentation

Cryptlib.Primitives.MKEM.Adapter

MKEM from a NIKE, an AEAD and a hash #

The only NIKE law used is NIKE.commutes.

ENCAPSULATE(keys, payload):
    eph            := GEN_KEYPAIR()
    secret_i       := HASH(DH(eph, keys[i]))            -- error if DH output is all zero
    msgKey         := RANDOM(32)
    envelope       := nonce ‖ AEAD_seal(msgKey, payload)
    dek_i          := nonce_i ‖ AEAD_seal(secret_i, msgKey)
    return eph, (eph_pub, [dek_i], envelope)

DECAPSULATE(sk, ct):
    secret         := HASH(DH(sk, ct.eph_pub))
    for dek in ct.deks: first one that opens under secret gives msgKey
    return open(msgKey, ct.envelope)

The pure functions take explicit randomness (Rand); the MKEM instance draws it from an Adapter.St seed stream, which must be unpredictable.

The three primitives, and the fact that lets a hash output be an AEAD key.

Instances For

    The sender's randomness, explicit so the pure construction is a function.

    Instances For

      List lemmas #

      theorem Cryptlib.Primitives.MKEM.Adapter.mapM_getElem {α β : Type} (f : α → Option β) (l : List α) (r : List β) :
      List.mapM f l = some r → ∃ (h : r.length = l.length), ∀ (i : Nat) (hi : i < l.length), f l[i] = some r[i]
      theorem Cryptlib.Primitives.MKEM.Adapter.findSome?_of_index {α β : Type} (f : α → Option β) (l : List α) (i : Nat) (hi : i < l.length) (x : β) :
      (∀ (j : Nat) (hj : j < i), f l[j] = none) → f l[i] = some x → List.findSome? f l = some x

      A raw byte string as an AEAD key, if it is the right length.

      Equations
      Instances For

        The hash of the Diffie-Hellman output; none if the output is all zero.

        Equations
        • One or more equations did not get rendered due to their size.
        Instances For

          Both parties derive the same key.

          Sealing under a key: nonce, then AEAD ciphertext #

          Equations
          • One or more equations did not get rendered due to their size.
          Instances For

            What was sealed opens.

            The construction, with explicit randomness #

            Encrypt payload to every recipient. none if any Diffie-Hellman output is degenerate.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For

              Open the first DEK that opens under the shared key, then the envelope.

              Equations
              • One or more equations did not get rendered due to their size.
              Instances For
                theorem Cryptlib.Primitives.MKEM.Adapter.Base.encapsulateP_ephPub (S : Base) (rand : Rand S) (keys : List { pk : S.nike.PublicKey // S.nike.Safe pk }) (payload : ByteArray) (eph : S.nike.PrivateKey) (ct : MKEM.Ciphertext S.nike.PublicKey) (h : S.encapsulateP rand keys payload = some (eph, ct)) :
                theorem Cryptlib.Primitives.MKEM.Adapter.Base.encapsulateP_spec (S : Base) (rand : Rand S) (keys : List { pk : S.nike.PublicKey // S.nike.Safe pk }) (payload : ByteArray) (eph : S.nike.PrivateKey) (ct : MKEM.Ciphertext S.nike.PublicKey) (h : S.encapsulateP rand keys payload = some (eph, ct)) :
                eph = S.nike.privateKeyFromSeed rand.seed ∧ ∃ (secrets : List S.aead.Key), List.mapM (S.deriveKey eph) keys = some secrets ∧ ct = { ephPub := S.nike.derivePublicKey eph, deks := List.map (fun (p : S.aead.Key × Vector UInt8 S.aead.nonceSize) => S.sealUnder p.fst p.snd (Util.Bytes.ofVector rand.msgKey)) (secrets.zip rand.dekNonces), envelope := S.sealUnder (S.aead.keyFromBytes rand.msgKey) rand.envNonce payload }

                Unpack a successful encapsulation.

                theorem Cryptlib.Primitives.MKEM.Adapter.Base.encapsulateP_shape (S : Base) (rand : Rand S) (keys : List { pk : S.nike.PublicKey // S.nike.Safe pk }) (payload : ByteArray) (eph : S.nike.PrivateKey) (ct : MKEM.Ciphertext S.nike.PublicKey) (hlen : rand.dekNonces.length = keys.length) (h : S.encapsulateP rand keys payload = some (eph, ct)) :
                ct.deks.length = keys.length ∧ (∀ (d : ByteArray), d ∈ ct.deks → d.size = S.aead.nonceSize + S.aead.keySize + S.aead.tagSize) ∧ ct.envelope.size = payload.size + (S.aead.nonceSize + S.aead.tagSize)

                One nonce + key + tag-byte DEK per recipient, and an envelope nonce + tag longer than the payload.

                theorem Cryptlib.Primitives.MKEM.Adapter.Base.decapsulateP_forRecipient (S : Base) (rand : Rand S) (keys : List { pk : S.nike.PublicKey // S.nike.Safe pk }) (payload : ByteArray) (eph : S.nike.PrivateKey) (ct : MKEM.Ciphertext S.nike.PublicKey) (hlen : rand.dekNonces.length = keys.length) (henc : S.encapsulateP rand keys payload = some (eph, ct)) (i : Nat) (hi : i < keys.length) (sk : S.nike.PrivateKey) (hsk : keys[i] = ⟨S.nike.derivePublicKey sk, ⋯⟩) :
                S.decapsulateP sk (ct.forRecipient i) = some payload

                Recipient i recovers the payload from the ciphertext carrying only its DEK.

                theorem Cryptlib.Primitives.MKEM.Adapter.Base.decapsulateP_encapsulateP_full (S : Base) (rand : Rand S) (keys : List { pk : S.nike.PublicKey // S.nike.Safe pk }) (payload : ByteArray) (eph : S.nike.PrivateKey) (ct : MKEM.Ciphertext S.nike.PublicKey) (hlen : rand.dekNonces.length = keys.length) (henc : S.encapsulateP rand keys payload = some (eph, ct)) (i : Nat) (hi : i < keys.length) (sk : S.nike.PrivateKey) (hsk : keys[i] = ⟨S.nike.derivePublicKey sk, ⋯⟩) (hcross : ∀ (k : S.aead.Key), S.deriveKey eph keys[i] = some k → ∀ (j : Nat), j < i → ∀ (hj' : j < ct.deks.length), S.unsealUnder k ct.deks[j] = none) :
                S.decapsulateP sk ct = some payload

                Recipient i recovers the payload from the full ciphertext, provided no earlier DEK opens under its key (hcross).

                Replies #

                Equations
                Instances For
                  Equations
                  Instances For

                    The MKEM instance #

                    i as 8 little-endian bytes.

                    Equations
                    Instances For

                      Randomness for encapsulating to n recipients, from stream position i: ephemeral seed, message key, envelope nonce, then one nonce per recipient.

                      Equations
                      • One or more equations did not get rendered due to their size.
                      Instances For
                        Equations
                        • One or more equations did not get rendered due to their size.
                        Instances For
                          theorem Cryptlib.Primitives.MKEM.Adapter.encapM_ok (S : Base) (keys : List { pk : S.nike.PublicKey // S.nike.Safe pk }) (payload : ByteArray) (s : KEM.Adapter.St) (r : S.nike.PrivateKey × MKEM.Ciphertext S.nike.PublicKey) (s' : KEM.Adapter.St) (h : encapM S keys payload s = EStateM.Result.ok r s') :
                          ∃ (rand : Rand S), rand.dekNonces.length = keys.length ∧ S.encapsulateP rand keys payload = some r
                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For
                            Equations
                            • One or more equations did not get rendered due to their size.
                            Instances For
                              Equations
                              • One or more equations did not get rendered due to their size.
                              Instances For

                                MKEM over the given NIKE, AEAD and hash.

                                Equations
                                • One or more equations did not get rendered due to their size.
                                Instances For

                                  MKEM over a NIKE, an AEAD, and a hash whose digest is an AEAD key.

                                  Equations
                                  Instances For