mirror of
https://github.com/XTLS/REALITY.git
synced 2026-09-29 02:17:56 +03:00
crypto/internal/hpke: modularize API and support more ciphersuites
Updates #75300 Change-Id: I6a6a6964de449b36bc6f5594e08c3c47a0a2f17f Reviewed-on: https://go-review.googlesource.com/c/go/+/701435 Reviewed-by: Daniel McCarney <daniel@binaryparadox.net> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Mark Freeman <markfreeman@google.com> Reviewed-by: Junyang Shao <shaojunyang@google.com>
This commit is contained in:
@@ -914,13 +914,29 @@ type Config struct {
|
||||
// with a specific ECH config known to a client.
|
||||
type EncryptedClientHelloKey struct {
|
||||
// Config should be a marshalled ECHConfig associated with PrivateKey. This
|
||||
// must match the config provided to clients byte-for-byte. The config
|
||||
// should only specify the DHKEM(X25519, HKDF-SHA256) KEM ID (0x0020), the
|
||||
// HKDF-SHA256 KDF ID (0x0001), and a subset of the following AEAD IDs:
|
||||
// AES-128-GCM (0x0001), AES-256-GCM (0x0002), ChaCha20Poly1305 (0x0003).
|
||||
// must match the config provided to clients byte-for-byte. The config must
|
||||
// use as KEM one of
|
||||
//
|
||||
// - DHKEM(P-256, HKDF-SHA256) (0x0010)
|
||||
// - DHKEM(P-384, HKDF-SHA384) (0x0011)
|
||||
// - DHKEM(P-521, HKDF-SHA512) (0x0012)
|
||||
// - DHKEM(X25519, HKDF-SHA256) (0x0020)
|
||||
//
|
||||
// and as KDF one of
|
||||
//
|
||||
// - HKDF-SHA256 (0x0001)
|
||||
// - HKDF-SHA384 (0x0002)
|
||||
// - HKDF-SHA512 (0x0003)
|
||||
//
|
||||
// and as AEAD one of
|
||||
//
|
||||
// - AES-128-GCM (0x0001)
|
||||
// - AES-256-GCM (0x0002)
|
||||
// - ChaCha20Poly1305 (0x0003)
|
||||
//
|
||||
Config []byte
|
||||
// PrivateKey should be a marshalled private key. Currently, we expect
|
||||
// this to be the output of [ecdh.PrivateKey.Bytes].
|
||||
// PrivateKey should be a marshalled private key, in the format expected by
|
||||
// HPKE's DeserializePrivateKey (see RFC 9180), for the KEM used in Config.
|
||||
PrivateKey []byte
|
||||
// SendAsRetry indicates if Config should be sent as part of the list of
|
||||
// retry configs when ECH is requested by the client but rejected by the
|
||||
|
||||
@@ -6,28 +6,14 @@ package reality
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hpke"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
|
||||
"github.com/xtls/reality/hpke"
|
||||
)
|
||||
|
||||
// sortedSupportedAEADs is just a sorted version of hpke.SupportedAEADS.
|
||||
// We need this so that when we insert them into ECHConfigs the ordering
|
||||
// is stable.
|
||||
var sortedSupportedAEADs []uint16
|
||||
|
||||
func init() {
|
||||
for aeadID := range hpke.SupportedAEADs {
|
||||
sortedSupportedAEADs = append(sortedSupportedAEADs, aeadID)
|
||||
}
|
||||
slices.Sort(sortedSupportedAEADs)
|
||||
}
|
||||
|
||||
type EchCipher struct {
|
||||
KDFID uint16
|
||||
AEADID uint16
|
||||
@@ -163,25 +149,8 @@ func parseECHConfigList(data []byte) ([]EchConfig, error) {
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
func pickECHConfig(list []EchConfig) *EchConfig {
|
||||
func pickECHConfig(list []echConfig) (*echConfig, hpke.KEMSender, hpke.KDF, hpke.AEAD) {
|
||||
for _, ec := range list {
|
||||
if _, ok := hpke.SupportedKEMs[ec.KemID]; !ok {
|
||||
continue
|
||||
}
|
||||
var validSCS bool
|
||||
for _, cs := range ec.SymmetricCipherSuite {
|
||||
if _, ok := hpke.SupportedAEADs[cs.AEADID]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := hpke.SupportedKDFs[cs.KDFID]; !ok {
|
||||
continue
|
||||
}
|
||||
validSCS = true
|
||||
break
|
||||
}
|
||||
if !validSCS {
|
||||
continue
|
||||
}
|
||||
if !validDNSName(string(ec.PublicName)) {
|
||||
continue
|
||||
}
|
||||
@@ -197,25 +166,26 @@ func pickECHConfig(list []EchConfig) *EchConfig {
|
||||
if unsupportedExt {
|
||||
continue
|
||||
}
|
||||
return &ec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func pickECHCipherSuite(suites []EchCipher) (EchCipher, error) {
|
||||
for _, s := range suites {
|
||||
// NOTE: all of the supported AEADs and KDFs are fine, rather than
|
||||
// imposing some sort of preference here, we just pick the first valid
|
||||
// suite.
|
||||
if _, ok := hpke.SupportedAEADs[s.AEADID]; !ok {
|
||||
s, err := hpke.NewKEMSender(ec.KemID, ec.PublicKey)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if _, ok := hpke.SupportedKDFs[s.KDFID]; !ok {
|
||||
continue
|
||||
for _, cs := range ec.SymmetricCipherSuite {
|
||||
// All of the supported AEADs and KDFs are fine, rather than
|
||||
// imposing some sort of preference here, we just pick the first
|
||||
// valid suite.
|
||||
kdf, err := hpke.NewKDF(cs.KDFID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
aead, err := hpke.NewAEAD(cs.AEADID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return &ec, s, kdf, aead
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return EchCipher{}, errors.New("tls: no supported symmetric ciphersuites for ECH")
|
||||
return nil, nil, nil, nil
|
||||
}
|
||||
|
||||
func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
|
||||
@@ -593,18 +563,28 @@ func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedC
|
||||
skip, config, err := parseECHConfig(echKey.Config)
|
||||
if err != nil || skip {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys Config: %s", err)
|
||||
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config: %s", err)
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
echPriv, err := hpke.ParseHPKEPrivateKey(config.KemID, echKey.PrivateKey)
|
||||
echPriv, err := hpke.NewKEMRecipient(config.KemID, echKey.PrivateKey)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys PrivateKey: %s", err)
|
||||
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey PrivateKey: %s", err)
|
||||
}
|
||||
kdf, err := hpke.NewKDF(echCiphersuite.KDFID)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KDF: %s", err)
|
||||
}
|
||||
aead, err := hpke.NewAEAD(echCiphersuite.AEADID)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config AEAD: %s", err)
|
||||
}
|
||||
info := append([]byte("tls ech\x00"), echKey.Config...)
|
||||
hpkeContext, err := hpke.SetupRecipient(hpke.DHKEM_X25519_HKDF_SHA256, echCiphersuite.KDFID, echCiphersuite.AEADID, echPriv, info, encap)
|
||||
hpkeContext, err := hpke.NewRecipient(encap, echPriv, kdf, aead, info)
|
||||
if err != nil {
|
||||
// attempt next trial decryption
|
||||
continue
|
||||
|
||||
+4
-13
@@ -10,6 +10,7 @@ import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/hpke"
|
||||
"crypto/mlkem"
|
||||
"crypto/rsa"
|
||||
"crypto/subtle"
|
||||
@@ -24,7 +25,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/xtls/reality/fips140tls"
|
||||
"github.com/xtls/reality/hpke"
|
||||
"github.com/xtls/reality/tls13"
|
||||
)
|
||||
|
||||
@@ -205,11 +205,11 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
echConfig := pickECHConfig(echConfigs)
|
||||
echConfig, echPK, kdf, aead := pickECHConfig(echConfigs)
|
||||
if echConfig == nil {
|
||||
return nil, nil, nil, errors.New("tls: EncryptedClientHelloConfigList contains no valid configs")
|
||||
}
|
||||
ech = &echClientContext{config: echConfig}
|
||||
ech = &echClientContext{config: echConfig, kdfID: kdf.ID(), aeadID: aead.ID()}
|
||||
hello.encryptedClientHello = []byte{1} // indicate inner hello
|
||||
// We need to explicitly set these 1.2 fields to nil, as we do not
|
||||
// marshal them when encoding the inner hello, otherwise transcripts
|
||||
@@ -219,17 +219,8 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
|
||||
hello.secureRenegotiationSupported = false
|
||||
hello.extendedMasterSecret = false
|
||||
|
||||
echPK, err := hpke.ParseHPKEPublicKey(ech.config.KemID, ech.config.PublicKey)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
suite, err := pickECHCipherSuite(ech.config.SymmetricCipherSuite)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
ech.kdfID, ech.aeadID = suite.KDFID, suite.AEADID
|
||||
info := append([]byte("tls ech\x00"), ech.config.raw...)
|
||||
ech.encapsulatedKey, ech.hpkeContext, err = hpke.SetupSender(ech.config.KemID, suite.KDFID, suite.AEADID, echPK, info)
|
||||
ech.encapsulatedKey, ech.hpkeContext, err = hpke.NewSender(echPK, kdf, aead, info)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"crypto/ed25519"
|
||||
"crypto/hkdf"
|
||||
"crypto/hmac"
|
||||
"crypto/hpke"
|
||||
"crypto/mlkem"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
@@ -29,7 +30,6 @@ import (
|
||||
|
||||
"github.com/cloudflare/circl/sign/mldsa/mldsa65"
|
||||
"github.com/xtls/reality/fips140tls"
|
||||
"github.com/xtls/reality/hpke"
|
||||
"github.com/xtls/reality/tls13"
|
||||
)
|
||||
|
||||
|
||||
-355
@@ -1,355 +0,0 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hpke
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdh"
|
||||
"crypto/hkdf"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
// testingOnlyGenerateKey is only used during testing, to provide
|
||||
// a fixed test key to use when checking the RFC 9180 vectors.
|
||||
var testingOnlyGenerateKey func() (*ecdh.PrivateKey, error)
|
||||
|
||||
type hkdfKDF struct {
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
func (kdf *hkdfKDF) LabeledExtract(sid []byte, salt []byte, label string, inputKey []byte) ([]byte, error) {
|
||||
labeledIKM := make([]byte, 0, 7+len(sid)+len(label)+len(inputKey))
|
||||
labeledIKM = append(labeledIKM, []byte("HPKE-v1")...)
|
||||
labeledIKM = append(labeledIKM, sid...)
|
||||
labeledIKM = append(labeledIKM, label...)
|
||||
labeledIKM = append(labeledIKM, inputKey...)
|
||||
return hkdf.Extract(kdf.hash.New, labeledIKM, salt)
|
||||
}
|
||||
|
||||
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 = append(labeledInfo, []byte("HPKE-v1")...)
|
||||
labeledInfo = append(labeledInfo, suiteID...)
|
||||
labeledInfo = append(labeledInfo, label...)
|
||||
labeledInfo = append(labeledInfo, info...)
|
||||
return hkdf.Expand(kdf.hash.New, randomKey, string(labeledInfo), int(length))
|
||||
}
|
||||
|
||||
// dhKEM implements the KEM specified in RFC 9180, Section 4.1.
|
||||
type dhKEM struct {
|
||||
dh ecdh.Curve
|
||||
kdf hkdfKDF
|
||||
|
||||
suiteID []byte
|
||||
nSecret uint16
|
||||
}
|
||||
|
||||
type KemID uint16
|
||||
|
||||
const DHKEM_X25519_HKDF_SHA256 = 0x0020
|
||||
|
||||
var SupportedKEMs = map[uint16]struct {
|
||||
curve ecdh.Curve
|
||||
hash crypto.Hash
|
||||
nSecret uint16
|
||||
}{
|
||||
// RFC 9180 Section 7.1
|
||||
DHKEM_X25519_HKDF_SHA256: {ecdh.X25519(), crypto.SHA256, 32},
|
||||
}
|
||||
|
||||
func newDHKem(kemID uint16) (*dhKEM, error) {
|
||||
suite, ok := SupportedKEMs[kemID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported suite ID")
|
||||
}
|
||||
return &dhKEM{
|
||||
dh: suite.curve,
|
||||
kdf: hkdfKDF{suite.hash},
|
||||
suiteID: binary.BigEndian.AppendUint16([]byte("KEM"), kemID),
|
||||
nSecret: suite.nSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (dh *dhKEM) ExtractAndExpand(dhKey, kemContext []byte) ([]byte, error) {
|
||||
eaePRK, err := dh.kdf.LabeledExtract(dh.suiteID[:], nil, "eae_prk", dhKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dh.kdf.LabeledExpand(dh.suiteID[:], eaePRK, "shared_secret", kemContext, dh.nSecret)
|
||||
}
|
||||
|
||||
func (dh *dhKEM) Encap(pubRecipient *ecdh.PublicKey) (sharedSecret []byte, encapPub []byte, err error) {
|
||||
var privEph *ecdh.PrivateKey
|
||||
if testingOnlyGenerateKey != nil {
|
||||
privEph, err = testingOnlyGenerateKey()
|
||||
} else {
|
||||
privEph, err = dh.dh.GenerateKey(rand.Reader)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
dhVal, err := privEph.ECDH(pubRecipient)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
encPubEph := privEph.PublicKey().Bytes()
|
||||
|
||||
encPubRecip := pubRecipient.Bytes()
|
||||
kemContext := append(encPubEph, encPubRecip...)
|
||||
sharedSecret, err = dh.ExtractAndExpand(dhVal, kemContext)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return sharedSecret, encPubEph, nil
|
||||
}
|
||||
|
||||
func (dh *dhKEM) Decap(encPubEph []byte, secRecipient *ecdh.PrivateKey) ([]byte, error) {
|
||||
pubEph, err := dh.dh.NewPublicKey(encPubEph)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dhVal, err := secRecipient.ECDH(pubEph)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kemContext := append(encPubEph, secRecipient.PublicKey().Bytes()...)
|
||||
return dh.ExtractAndExpand(dhVal, kemContext)
|
||||
}
|
||||
|
||||
type context struct {
|
||||
aead cipher.AEAD
|
||||
|
||||
sharedSecret []byte
|
||||
|
||||
suiteID []byte
|
||||
|
||||
key []byte
|
||||
baseNonce []byte
|
||||
exporterSecret []byte
|
||||
|
||||
seqNum uint128
|
||||
}
|
||||
|
||||
type Sender struct {
|
||||
*context
|
||||
}
|
||||
|
||||
type Recipient struct {
|
||||
*context
|
||||
}
|
||||
|
||||
var aesGCMNew = func(key []byte) (cipher.AEAD, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cipher.NewGCM(block)
|
||||
}
|
||||
|
||||
type AEADID uint16
|
||||
|
||||
const (
|
||||
AEAD_AES_128_GCM = 0x0001
|
||||
AEAD_AES_256_GCM = 0x0002
|
||||
AEAD_ChaCha20Poly1305 = 0x0003
|
||||
)
|
||||
|
||||
var SupportedAEADs = map[uint16]struct {
|
||||
keySize int
|
||||
nonceSize int
|
||||
aead func([]byte) (cipher.AEAD, error)
|
||||
}{
|
||||
// RFC 9180, Section 7.3
|
||||
AEAD_AES_128_GCM: {keySize: 16, nonceSize: 12, aead: aesGCMNew},
|
||||
AEAD_AES_256_GCM: {keySize: 32, nonceSize: 12, aead: aesGCMNew},
|
||||
AEAD_ChaCha20Poly1305: {keySize: chacha20poly1305.KeySize, nonceSize: chacha20poly1305.NonceSize, aead: chacha20poly1305.New},
|
||||
}
|
||||
|
||||
type KDFID uint16
|
||||
|
||||
const KDF_HKDF_SHA256 = 0x0001
|
||||
|
||||
var SupportedKDFs = map[uint16]func() *hkdfKDF{
|
||||
// RFC 9180, Section 7.2
|
||||
KDF_HKDF_SHA256: func() *hkdfKDF { return &hkdfKDF{crypto.SHA256} },
|
||||
}
|
||||
|
||||
func newContext(sharedSecret []byte, kemID, kdfID, aeadID uint16, info []byte) (*context, error) {
|
||||
sid := suiteID(kemID, kdfID, aeadID)
|
||||
|
||||
kdfInit, ok := SupportedKDFs[kdfID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported KDF id")
|
||||
}
|
||||
kdf := kdfInit()
|
||||
|
||||
aeadInfo, ok := SupportedAEADs[aeadID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported AEAD id")
|
||||
}
|
||||
|
||||
pskIDHash, err := kdf.LabeledExtract(sid, nil, "psk_id_hash", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infoHash, err := kdf.LabeledExtract(sid, nil, "info_hash", info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ksContext := append([]byte{0}, pskIDHash...)
|
||||
ksContext = append(ksContext, infoHash...)
|
||||
|
||||
secret, err := kdf.LabeledExtract(sid, sharedSecret, "secret", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := kdf.LabeledExpand(sid, secret, "key", ksContext, uint16(aeadInfo.keySize) /* Nk - key size for AEAD */)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseNonce, err := kdf.LabeledExpand(sid, secret, "base_nonce", ksContext, uint16(aeadInfo.nonceSize) /* Nn - nonce size for AEAD */)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exporterSecret, err := kdf.LabeledExpand(sid, secret, "exp", ksContext, uint16(kdf.hash.Size()) /* Nh - hash output size of the kdf*/)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aead, err := aeadInfo.aead(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &context{
|
||||
aead: aead,
|
||||
sharedSecret: sharedSecret,
|
||||
suiteID: sid,
|
||||
key: key,
|
||||
baseNonce: baseNonce,
|
||||
exporterSecret: exporterSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SetupSender(kemID, kdfID, aeadID uint16, pub *ecdh.PublicKey, info []byte) ([]byte, *Sender, error) {
|
||||
kem, err := newDHKem(kemID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sharedSecret, encapsulatedKey, err := kem.Encap(pub)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return encapsulatedKey, &Sender{context}, nil
|
||||
}
|
||||
|
||||
func SetupRecipient(kemID, kdfID, aeadID uint16, priv *ecdh.PrivateKey, info, encPubEph []byte) (*Recipient, error) {
|
||||
kem, err := newDHKem(kemID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sharedSecret, err := kem.Decap(encPubEph, priv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Recipient{context}, nil
|
||||
}
|
||||
|
||||
func (ctx *context) nextNonce() []byte {
|
||||
nonce := ctx.seqNum.bytes()[16-ctx.aead.NonceSize():]
|
||||
for i := range ctx.baseNonce {
|
||||
nonce[i] ^= ctx.baseNonce[i]
|
||||
}
|
||||
return nonce
|
||||
}
|
||||
|
||||
func (ctx *context) incrementNonce() {
|
||||
// Message limit is, according to the RFC, 2^95+1, which
|
||||
// is somewhat confusing, but we do as we're told.
|
||||
if ctx.seqNum.bitLen() >= (ctx.aead.NonceSize()*8)-1 {
|
||||
panic("message limit reached")
|
||||
}
|
||||
ctx.seqNum = ctx.seqNum.addOne()
|
||||
}
|
||||
|
||||
func (s *Sender) Seal(aad, plaintext []byte) ([]byte, error) {
|
||||
ciphertext := s.aead.Seal(nil, s.nextNonce(), plaintext, aad)
|
||||
s.incrementNonce()
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func (r *Recipient) Open(aad, ciphertext []byte) ([]byte, error) {
|
||||
plaintext, err := r.aead.Open(nil, r.nextNonce(), ciphertext, aad)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.incrementNonce()
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
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)
|
||||
return suiteID
|
||||
}
|
||||
|
||||
func ParseHPKEPublicKey(kemID uint16, bytes []byte) (*ecdh.PublicKey, error) {
|
||||
kemInfo, ok := SupportedKEMs[kemID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported KEM id")
|
||||
}
|
||||
return kemInfo.curve.NewPublicKey(bytes)
|
||||
}
|
||||
|
||||
func ParseHPKEPrivateKey(kemID uint16, bytes []byte) (*ecdh.PrivateKey, error) {
|
||||
kemInfo, ok := SupportedKEMs[kemID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported KEM id")
|
||||
}
|
||||
return kemInfo.curve.NewPrivateKey(bytes)
|
||||
}
|
||||
|
||||
type uint128 struct {
|
||||
hi, lo uint64
|
||||
}
|
||||
|
||||
func (u uint128) addOne() uint128 {
|
||||
lo, carry := bits.Add64(u.lo, 1, 0)
|
||||
return uint128{u.hi + carry, lo}
|
||||
}
|
||||
|
||||
func (u uint128) bitLen() int {
|
||||
return bits.Len64(u.hi) + bits.Len64(u.lo)
|
||||
}
|
||||
|
||||
func (u uint128) bytes() []byte {
|
||||
b := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(b[0:], u.hi)
|
||||
binary.BigEndian.PutUint64(b[8:], u.lo)
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user