diff --git a/api/next/75300.txt b/api/next/75300.txt index da24eb4aa3..5e418e525c 100644 --- a/api/next/75300.txt +++ b/api/next/75300.txt @@ -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 index 0000000000..ee621eee35 --- /dev/null +++ b/doc/next/6-stdlib/50-hpke.md @@ -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 index 0000000000..e769ca7802 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/crypto/hpke/75300.md @@ -0,0 +1 @@ + diff --git a/src/crypto/internal/hpke/aead.go b/src/crypto/hpke/aead.go similarity index 100% rename from src/crypto/internal/hpke/aead.go rename to src/crypto/hpke/aead.go diff --git a/src/crypto/internal/hpke/hpke.go b/src/crypto/hpke/hpke.go similarity index 92% rename from src/crypto/internal/hpke/hpke.go rename to src/crypto/hpke/hpke.go index d2df248400..5ad3b8a4ca 100644 --- a/src/crypto/internal/hpke/hpke.go +++ b/src/crypto/hpke/hpke.go @@ -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 } diff --git a/src/crypto/internal/hpke/hpke_test.go b/src/crypto/hpke/hpke_test.go similarity index 100% rename from src/crypto/internal/hpke/hpke_test.go rename to src/crypto/hpke/hpke_test.go diff --git a/src/crypto/internal/hpke/kdf.go b/src/crypto/hpke/kdf.go similarity index 98% rename from src/crypto/internal/hpke/kdf.go rename to src/crypto/hpke/kdf.go index 7862dd22a2..23217a718d 100644 --- a/src/crypto/internal/hpke/kdf.go +++ b/src/crypto/hpke/kdf.go @@ -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...) diff --git a/src/crypto/internal/hpke/kem.go b/src/crypto/hpke/kem.go similarity index 98% rename from src/crypto/internal/hpke/kem.go rename to src/crypto/hpke/kem.go index 4aa87fc0b4..c30f79bad4 100644 --- a/src/crypto/internal/hpke/kem.go +++ b/src/crypto/hpke/kem.go @@ -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 diff --git a/src/crypto/internal/hpke/pq.go b/src/crypto/hpke/pq.go similarity index 98% rename from src/crypto/internal/hpke/pq.go rename to src/crypto/hpke/pq.go index dfcd084ab7..322f937ae8 100644 --- a/src/crypto/internal/hpke/pq.go +++ b/src/crypto/hpke/pq.go @@ -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 diff --git a/src/crypto/internal/hpke/testdata/hpke-pq.json b/src/crypto/hpke/testdata/hpke-pq.json similarity index 100% rename from src/crypto/internal/hpke/testdata/hpke-pq.json rename to src/crypto/hpke/testdata/hpke-pq.json diff --git a/src/crypto/internal/hpke/testdata/rfc9180.json b/src/crypto/hpke/testdata/rfc9180.json similarity index 100% rename from src/crypto/internal/hpke/testdata/rfc9180.json rename to src/crypto/hpke/testdata/rfc9180.json diff --git a/src/crypto/tls/ech.go b/src/crypto/tls/ech.go index fb02197e2b..adeff9e050 100644 --- a/src/crypto/tls/ech.go +++ b/src/crypto/tls/ech.go @@ -6,7 +6,7 @@ package tls import ( "bytes" - "crypto/internal/hpke" + "crypto/hpke" "errors" "fmt" "strings" diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index 47cf88323d..c739544db6 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -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" diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index c5b0552cae..c227371ace 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -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" diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 9a6b86b65c..b02db785c1 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -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