]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/hpke: expose crypto/internal/hpke
authorFilippo Valsorda <filippo@golang.org>
Sat, 22 Nov 2025 22:03:14 +0000 (23:03 +0100)
committerGopher Robot <gobot@golang.org>
Tue, 25 Nov 2025 18:35:07 +0000 (10:35 -0800)
Fixes #75300

Change-Id: I6a83e0d040dba3366819d2afff704f886a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/723560
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
TryBot-Bypass: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
15 files changed:
api/next/75300.txt
doc/next/6-stdlib/50-hpke.md [new file with mode: 0644]
doc/next/6-stdlib/99-minor/crypto/hpke/75300.md [new file with mode: 0644]
src/crypto/hpke/aead.go [moved from src/crypto/internal/hpke/aead.go with 100% similarity]
src/crypto/hpke/hpke.go [moved from src/crypto/internal/hpke/hpke.go with 92% similarity]
src/crypto/hpke/hpke_test.go [moved from src/crypto/internal/hpke/hpke_test.go with 100% similarity]
src/crypto/hpke/kdf.go [moved from src/crypto/internal/hpke/kdf.go with 98% similarity]
src/crypto/hpke/kem.go [moved from src/crypto/internal/hpke/kem.go with 98% similarity]
src/crypto/hpke/pq.go [moved from src/crypto/internal/hpke/pq.go with 98% similarity]
src/crypto/hpke/testdata/hpke-pq.json [moved from src/crypto/internal/hpke/testdata/hpke-pq.json with 100% similarity]
src/crypto/hpke/testdata/rfc9180.json [moved from src/crypto/internal/hpke/testdata/rfc9180.json with 100% similarity]
src/crypto/tls/ech.go
src/crypto/tls/handshake_client.go
src/crypto/tls/handshake_server_tls13.go
src/go/build/deps_test.go

index da24eb4aa341374be9bd14fb2d7a91e18d9d3f06..5e418e525c1bc08549a3b1991267465f00f20cf8 100644 (file)
@@ -10,3 +10,54 @@ pkg crypto/ecdh, type KeyExchanger interface, ECDH(*PublicKey) ([]uint8, error)
 pkg crypto/ecdh, type KeyExchanger interface, PublicKey() *PublicKey #75300
 pkg crypto/mlkem, method (*DecapsulationKey1024) Encapsulator() crypto.Encapsulator #75300
 pkg crypto/mlkem, method (*DecapsulationKey768) Encapsulator() crypto.Encapsulator #75300
+pkg crypto/hpke, func AES128GCM() AEAD #75300
+pkg crypto/hpke, func AES256GCM() AEAD #75300
+pkg crypto/hpke, func ChaCha20Poly1305() AEAD #75300
+pkg crypto/hpke, func DHKEM(ecdh.Curve) KEM #75300
+pkg crypto/hpke, func ExportOnly() AEAD #75300
+pkg crypto/hpke, func HKDFSHA256() KDF #75300
+pkg crypto/hpke, func HKDFSHA384() KDF #75300
+pkg crypto/hpke, func HKDFSHA512() KDF #75300
+pkg crypto/hpke, func MLKEM1024() KEM #75300
+pkg crypto/hpke, func MLKEM1024P384() KEM #75300
+pkg crypto/hpke, func MLKEM768() KEM #75300
+pkg crypto/hpke, func MLKEM768P256() KEM #75300
+pkg crypto/hpke, func MLKEM768X25519() KEM #75300
+pkg crypto/hpke, func NewAEAD(uint16) (AEAD, error) #75300
+pkg crypto/hpke, func NewDHKEMPrivateKey(ecdh.KeyExchanger) (PrivateKey, error) #75300
+pkg crypto/hpke, func NewDHKEMPublicKey(*ecdh.PublicKey) (PublicKey, error) #75300
+pkg crypto/hpke, func NewHybridPrivateKey(crypto.Decapsulator, ecdh.KeyExchanger) (PrivateKey, error) #75300
+pkg crypto/hpke, func NewHybridPublicKey(crypto.Encapsulator, *ecdh.PublicKey) (PublicKey, error) #75300
+pkg crypto/hpke, func NewKDF(uint16) (KDF, error) #75300
+pkg crypto/hpke, func NewKEM(uint16) (KEM, error) #75300
+pkg crypto/hpke, func NewMLKEMPrivateKey(crypto.Decapsulator) (PrivateKey, error) #75300
+pkg crypto/hpke, func NewMLKEMPublicKey(crypto.Encapsulator) (PublicKey, error) #75300
+pkg crypto/hpke, func NewRecipient([]uint8, PrivateKey, KDF, AEAD, []uint8) (*Recipient, error) #75300
+pkg crypto/hpke, func NewSender(PublicKey, KDF, AEAD, []uint8) ([]uint8, *Sender, error) #75300
+pkg crypto/hpke, func Open(PrivateKey, KDF, AEAD, []uint8, []uint8) ([]uint8, error) #75300
+pkg crypto/hpke, func SHAKE128() KDF #75300
+pkg crypto/hpke, func SHAKE256() KDF #75300
+pkg crypto/hpke, func Seal(PublicKey, KDF, AEAD, []uint8, []uint8) ([]uint8, error) #75300
+pkg crypto/hpke, method (*Recipient) Export(string, int) ([]uint8, error) #75300
+pkg crypto/hpke, method (*Recipient) Open([]uint8, []uint8) ([]uint8, error) #75300
+pkg crypto/hpke, method (*Sender) Export(string, int) ([]uint8, error) #75300
+pkg crypto/hpke, method (*Sender) Seal([]uint8, []uint8) ([]uint8, error) #75300
+pkg crypto/hpke, type AEAD interface, ID() uint16 #75300
+pkg crypto/hpke, type AEAD interface, unexported methods #75300
+pkg crypto/hpke, type KDF interface, ID() uint16 #75300
+pkg crypto/hpke, type KDF interface, unexported methods #75300
+pkg crypto/hpke, type KEM interface, DeriveKeyPair([]uint8) (PrivateKey, error) #75300
+pkg crypto/hpke, type KEM interface, GenerateKey() (PrivateKey, error) #75300
+pkg crypto/hpke, type KEM interface, ID() uint16 #75300
+pkg crypto/hpke, type KEM interface, NewPrivateKey([]uint8) (PrivateKey, error) #75300
+pkg crypto/hpke, type KEM interface, NewPublicKey([]uint8) (PublicKey, error) #75300
+pkg crypto/hpke, type KEM interface, unexported methods #75300
+pkg crypto/hpke, type PrivateKey interface, Bytes() ([]uint8, error) #75300
+pkg crypto/hpke, type PrivateKey interface, KEM() KEM #75300
+pkg crypto/hpke, type PrivateKey interface, PublicKey() PublicKey #75300
+pkg crypto/hpke, type PrivateKey interface, unexported methods #75300
+pkg crypto/hpke, type PublicKey interface, Bytes() []uint8 #75300
+pkg crypto/hpke, type PublicKey interface, KEM() KEM #75300
+pkg crypto/hpke, type PublicKey interface, unexported methods #75300
+pkg crypto/hpke, type Recipient struct #75300
+pkg crypto/hpke, type Sender struct #75300
diff --git a/doc/next/6-stdlib/50-hpke.md b/doc/next/6-stdlib/50-hpke.md
new file mode 100644 (file)
index 0000000..ee621ee
--- /dev/null
@@ -0,0 +1,7 @@
+### crypto/hpke
+
+The new [crypto/hpke] package implements Hybrid Public Key Encryption
+(HPKE) as specified in [RFC 9180], including support for post-quantum
+hybrid KEMs.
+
+[RFC 9180]: https://rfc-editor.org/rfc/rfc9180.html
diff --git a/doc/next/6-stdlib/99-minor/crypto/hpke/75300.md b/doc/next/6-stdlib/99-minor/crypto/hpke/75300.md
new file mode 100644 (file)
index 0000000..e769ca7
--- /dev/null
@@ -0,0 +1 @@
+<!-- crypto/hpke is documented in its own section. -->
similarity index 92%
rename from src/crypto/internal/hpke/hpke.go
rename to src/crypto/hpke/hpke.go
index d2df2484003fe546fa39e21c6feedccdcf6e0b7d..5ad3b8a4ca9176c1b68ff32be20e8c4e06b211c3 100644 (file)
@@ -10,8 +10,8 @@ package hpke
 
 import (
        "crypto/cipher"
-       "encoding/binary"
        "errors"
+       "internal/byteorder"
 )
 
 type context struct {
@@ -45,14 +45,14 @@ func newContext(sharedSecret []byte, kemID uint16, kdf KDF, aead AEAD, info []by
 
        if kdf.oneStage() {
                secrets := make([]byte, 0, 2+2+len(sharedSecret))
-               secrets = binary.BigEndian.AppendUint16(secrets, 0) // empty psk
-               secrets = binary.BigEndian.AppendUint16(secrets, uint16(len(sharedSecret)))
+               secrets = byteorder.BEAppendUint16(secrets, 0) // empty psk
+               secrets = byteorder.BEAppendUint16(secrets, uint16(len(sharedSecret)))
                secrets = append(secrets, sharedSecret...)
 
                ksContext := make([]byte, 0, 1+2+2+len(info))
-               ksContext = append(ksContext, 0)                        // mode 0
-               ksContext = binary.BigEndian.AppendUint16(ksContext, 0) // empty psk_id
-               ksContext = binary.BigEndian.AppendUint16(ksContext, uint16(len(info)))
+               ksContext = append(ksContext, 0)                   // mode 0
+               ksContext = byteorder.BEAppendUint16(ksContext, 0) // empty psk_id
+               ksContext = byteorder.BEAppendUint16(ksContext, uint16(len(info)))
                ksContext = append(ksContext, info...)
 
                secret, err := kdf.labeledDerive(sid, secrets, "secret", ksContext,
@@ -245,7 +245,7 @@ func (r *Recipient) Export(exporterContext string, length int) ([]byte, error) {
 
 func (ctx *context) nextNonce() []byte {
        nonce := make([]byte, ctx.aead.NonceSize())
-       binary.BigEndian.PutUint64(nonce[len(nonce)-8:], ctx.seqNum)
+       byteorder.BEPutUint64(nonce[len(nonce)-8:], ctx.seqNum)
        for i := range ctx.baseNonce {
                nonce[i] ^= ctx.baseNonce[i]
        }
@@ -255,8 +255,8 @@ func (ctx *context) nextNonce() []byte {
 func suiteID(kemID, kdfID, aeadID uint16) []byte {
        suiteID := make([]byte, 0, 4+2+2+2)
        suiteID = append(suiteID, []byte("HPKE")...)
-       suiteID = binary.BigEndian.AppendUint16(suiteID, kemID)
-       suiteID = binary.BigEndian.AppendUint16(suiteID, kdfID)
-       suiteID = binary.BigEndian.AppendUint16(suiteID, aeadID)
+       suiteID = byteorder.BEAppendUint16(suiteID, kemID)
+       suiteID = byteorder.BEAppendUint16(suiteID, kdfID)
+       suiteID = byteorder.BEAppendUint16(suiteID, aeadID)
        return suiteID
 }
similarity index 98%
rename from src/crypto/internal/hpke/kdf.go
rename to src/crypto/hpke/kdf.go
index 7862dd22a2a9b3ad8235e4e42e0b7f104df2c07e..23217a718dc8ae7fe87f1a708136de7292b2724d 100644 (file)
@@ -9,10 +9,10 @@ import (
        "crypto/sha256"
        "crypto/sha3"
        "crypto/sha512"
-       "encoding/binary"
        "errors"
        "fmt"
        "hash"
+       "internal/byteorder"
 )
 
 // The KDF is one of the three components of an HPKE ciphersuite, implementing
@@ -93,7 +93,7 @@ func (kdf *hkdfKDF) labeledExtract(suiteID []byte, salt []byte, label string, in
 
 func (kdf *hkdfKDF) labeledExpand(suiteID []byte, randomKey []byte, label string, info []byte, length uint16) ([]byte, error) {
        labeledInfo := make([]byte, 0, 2+7+len(suiteID)+len(label)+len(info))
-       labeledInfo = binary.BigEndian.AppendUint16(labeledInfo, length)
+       labeledInfo = byteorder.BEAppendUint16(labeledInfo, length)
        labeledInfo = append(labeledInfo, []byte("HPKE-v1")...)
        labeledInfo = append(labeledInfo, suiteID...)
        labeledInfo = append(labeledInfo, label...)
similarity index 98%
rename from src/crypto/internal/hpke/kem.go
rename to src/crypto/hpke/kem.go
index 4aa87fc0b444ba87b3015ad7b7bf027baaf0b1b7..c30f79bad494bf5e0b22155d675774a7039e3533 100644 (file)
@@ -7,8 +7,8 @@ package hpke
 import (
        "crypto/ecdh"
        "crypto/rand"
-       "encoding/binary"
        "errors"
+       "internal/byteorder"
 )
 
 // A KEM is a Key Encapsulation Mechanism, one of the three components of an
@@ -114,7 +114,7 @@ type dhKEM struct {
 }
 
 func (kem *dhKEM) extractAndExpand(dhKey, kemContext []byte) ([]byte, error) {
-       suiteID := binary.BigEndian.AppendUint16([]byte("KEM"), kem.id)
+       suiteID := byteorder.BEAppendUint16([]byte("KEM"), kem.id)
        eaePRK, err := kem.kdf.labeledExtract(suiteID, nil, "eae_prk", dhKey)
        if err != nil {
                return nil, err
@@ -302,7 +302,7 @@ func (kem *dhKEM) NewPrivateKey(ikm []byte) (PrivateKey, error) {
 
 func (kem *dhKEM) DeriveKeyPair(ikm []byte) (PrivateKey, error) {
        // DeriveKeyPair from RFC 9180 Section 7.1.3.
-       suiteID := binary.BigEndian.AppendUint16([]byte("KEM"), kem.id)
+       suiteID := byteorder.BEAppendUint16([]byte("KEM"), kem.id)
        prk, err := kem.kdf.labeledExtract(suiteID, nil, "dkp_prk", ikm)
        if err != nil {
                return nil, err
similarity index 98%
rename from src/crypto/internal/hpke/pq.go
rename to src/crypto/hpke/pq.go
index dfcd084ab71d0cbaf59cdcefd2712c9e8ad7dadc..322f937ae8608b9a35f60f09ccb10dcedaacd380 100644 (file)
@@ -11,8 +11,8 @@ import (
        "crypto/mlkem"
        "crypto/rand"
        "crypto/sha3"
-       "encoding/binary"
        "errors"
+       "internal/byteorder"
 )
 
 var mlkem768X25519 = &hybridKEM{
@@ -299,7 +299,7 @@ func newHybridPrivateKey(pq crypto.Decapsulator, t ecdh.KeyExchanger, seed []byt
 }
 
 func (kem *hybridKEM) DeriveKeyPair(ikm []byte) (PrivateKey, error) {
-       suiteID := binary.BigEndian.AppendUint16([]byte("KEM"), kem.id)
+       suiteID := byteorder.BEAppendUint16([]byte("KEM"), kem.id)
        dk, err := SHAKE256().labeledDerive(suiteID, ikm, "DeriveKeyPair", nil, 32)
        if err != nil {
                return nil, err
@@ -496,7 +496,7 @@ func (kem *mlkemKEM) NewPrivateKey(priv []byte) (PrivateKey, error) {
 }
 
 func (kem *mlkemKEM) DeriveKeyPair(ikm []byte) (PrivateKey, error) {
-       suiteID := binary.BigEndian.AppendUint16([]byte("KEM"), kem.id)
+       suiteID := byteorder.BEAppendUint16([]byte("KEM"), kem.id)
        dk, err := SHAKE256().labeledDerive(suiteID, ikm, "DeriveKeyPair", nil, 64)
        if err != nil {
                return nil, err
index fb02197e2babb8ecfd7fa453856e7933d112544e..adeff9e0508bd9cda76598c4499d0af9b10c1550 100644 (file)
@@ -6,7 +6,7 @@ package tls
 
 import (
        "bytes"
-       "crypto/internal/hpke"
+       "crypto/hpke"
        "errors"
        "fmt"
        "strings"
index 47cf88323d802ebdfe3dd725c0913dc9ba01f832..c739544db678e7f54ba2b538de2f3acc864e4f23 100644 (file)
@@ -10,9 +10,9 @@ import (
        "crypto"
        "crypto/ecdsa"
        "crypto/ed25519"
+       "crypto/hpke"
        "crypto/internal/fips140/mlkem"
        "crypto/internal/fips140/tls13"
-       "crypto/internal/hpke"
        "crypto/rsa"
        "crypto/subtle"
        "crypto/tls/internal/fips140tls"
index c5b0552cae6134d6ec12e69b26f74d5bd2ad0183..c227371aceb9426f5484712c5f7e007ffb4b4b1b 100644 (file)
@@ -10,9 +10,9 @@ import (
        "crypto"
        "crypto/hkdf"
        "crypto/hmac"
+       "crypto/hpke"
        "crypto/internal/fips140/mlkem"
        "crypto/internal/fips140/tls13"
-       "crypto/internal/hpke"
        "crypto/rsa"
        "crypto/tls/internal/fips140tls"
        "errors"
index 9a6b86b65c86a9b5999b03e73a1146e8909a71a3..b02db785c13a34d4ea26b11eb50645bebea3b56d 100644 (file)
@@ -604,9 +604,11 @@ var depsRules = `
        < golang.org/x/crypto/internal/poly1305
        < golang.org/x/crypto/chacha20poly1305;
 
-       CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem,
+       CRYPTO-MATH, golang.org/x/crypto/chacha20poly1305
+       < crypto/hpke;
+
+       CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem, crypto/hpke,
        golang.org/x/crypto/chacha20poly1305, crypto/tls/internal/fips140tls
-       < crypto/internal/hpke
        < crypto/x509/internal/macos
        < crypto/x509/pkix
        < crypto/x509