Class Hpke

java.lang.Object
com.yahoo.security.hpke.Hpke

public final class Hpke extends Object
Restricted subset implementation of RFC 9180 Hybrid Public Key Encryption (HPKE)

HPKE is an encryption scheme that builds around three primitives:

  • A key encapsulation mechanism (KEM)
  • A key derivation function (KDF)
  • An "authenticated encryption with associated data" (AEAD) algorithm

The 3-tuple (KEM, KDF, AEAD) is known as the HPKE ciphersuite.

This implementation has certain (intentional) limitations:

  • Only the DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM ciphersuite is implemented. This is expected to be a good default choice for any internal use of this class.
  • Only the "base" (unauthenticated sender) and "auth" (authentication using an asymmetric key) modes are supported, i.e. no PSK support and no secret exporting. This implementation is only expected to be used for one-way encryption (possibly authenticated, if using the "auth" mode).
  • The API only offers single-shot encryption to keep anyone from being tempted to use it to build their own multi-message protocol on top. This entirely avoids the risk of nonce reuse caused by accidentally repeating sequence numbers.

Deprecation notice: once BouncyCastle (or the Java crypto API) supports HPKE, this particular implementation can safely be deprecated and sent off to live on a farm.

Author:
vekterli
See Also:
  • Method Details

    • of

      public static Hpke of(Ciphersuite ciphersuite)
    • sealBase

      public Hpke.Sealed sealBase(XECPublicKey pkR, byte[] info, byte[] aad, byte[] pt)
      Section 6.1 Encryption and Decryption:
       def Seal<MODE>(pkR, info, aad, pt, ...):
         enc, ctx = Setup<MODE>S(pkR, info, ...)
         ct = ctx.Seal(aad, pt)
         return enc, ct
       
      Section 5.2 Encryption and Decryption: Since we only support single-shot encryption we collapse ContextS.Seal into the parent SealBASE, since we don't have to track sequence numbers. This means ComputeNonce is a no-op since the first sequence number is 0 which will always XOR to the same nonce.
       def ContextS.Seal(aad, pt):
         ct = Seal(self.key, self.ComputeNonce(self.seq), aad, pt)
         self.IncrementSeq()
         return ct
       
    • sealAuth

      public Hpke.Sealed sealAuth(XECPublicKey pkR, byte[] info, byte[] aad, byte[] pt, XECPrivateKey skS)
    • openBase

      public byte[] openBase(byte[] enc, XECPrivateKey skR, byte[] info, byte[] aad, byte[] ct)
      Section 6.1 Encryption and Decryption:
       def Open<MODE>(enc, skR, info, aad, ct, ...):
         ctx = Setup<MODE>R(enc, skR, info, ...)
         return ctx.Open(aad, ct)
       
      Section 5.2 Encryption and Decryption: Since we only support single-shot decryption we collapse ContextR.Open into the parent OpenBASE, since we don't have to track sequence numbers. See also: sealBase()
       def ContextR.Open(aad, ct):
         pt = Open(self.key, self.ComputeNonce(self.seq), aad, ct)
         if pt == OpenError:
           raise OpenError
         self.IncrementSeq()
         return pt
       
    • openAuth

      public byte[] openAuth(byte[] enc, XECPrivateKey skR, byte[] info, byte[] aad, byte[] ct, XECPublicKey pkS)