Source file src/crypto/hpke/kdf.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package hpke
     6  
     7  import (
     8  	"crypto/hkdf"
     9  	"crypto/sha256"
    10  	"crypto/sha3"
    11  	"crypto/sha512"
    12  	"errors"
    13  	"fmt"
    14  	"hash"
    15  	"internal/byteorder"
    16  )
    17  
    18  // The KDF is one of the three components of an HPKE ciphersuite, implementing
    19  // key derivation.
    20  type KDF interface {
    21  	ID() uint16
    22  	oneStage() bool
    23  	size() int // Nh
    24  	labeledDerive(suiteID, inputKey []byte, label string, context []byte, length uint16) ([]byte, error)
    25  	labeledExtract(suiteID, salt []byte, label string, inputKey []byte) ([]byte, error)
    26  	labeledExpand(suiteID, randomKey []byte, label string, info []byte, length uint16) ([]byte, error)
    27  }
    28  
    29  // NewKDF returns the KDF implementation for the given KDF ID.
    30  //
    31  // Applications are encouraged to use specific implementations like [HKDFSHA256]
    32  // instead, unless runtime agility is required.
    33  func NewKDF(id uint16) (KDF, error) {
    34  	switch id {
    35  	case 0x0001: // HKDF-SHA256
    36  		return HKDFSHA256(), nil
    37  	case 0x0002: // HKDF-SHA384
    38  		return HKDFSHA384(), nil
    39  	case 0x0003: // HKDF-SHA512
    40  		return HKDFSHA512(), nil
    41  	case 0x0010: // SHAKE128
    42  		return SHAKE128(), nil
    43  	case 0x0011: // SHAKE256
    44  		return SHAKE256(), nil
    45  	default:
    46  		return nil, fmt.Errorf("unsupported KDF %04x", id)
    47  	}
    48  }
    49  
    50  // HKDFSHA256 returns an HKDF-SHA256 KDF implementation.
    51  func HKDFSHA256() KDF { return hkdfSHA256 }
    52  
    53  // HKDFSHA384 returns an HKDF-SHA384 KDF implementation.
    54  func HKDFSHA384() KDF { return hkdfSHA384 }
    55  
    56  // HKDFSHA512 returns an HKDF-SHA512 KDF implementation.
    57  func HKDFSHA512() KDF { return hkdfSHA512 }
    58  
    59  type hkdfKDF struct {
    60  	hash func() hash.Hash
    61  	id   uint16
    62  	nH   int
    63  }
    64  
    65  var hkdfSHA256 = &hkdfKDF{hash: sha256.New, id: 0x0001, nH: sha256.Size}
    66  var hkdfSHA384 = &hkdfKDF{hash: sha512.New384, id: 0x0002, nH: sha512.Size384}
    67  var hkdfSHA512 = &hkdfKDF{hash: sha512.New, id: 0x0003, nH: sha512.Size}
    68  
    69  func (kdf *hkdfKDF) ID() uint16 {
    70  	return kdf.id
    71  }
    72  
    73  func (kdf *hkdfKDF) size() int {
    74  	return kdf.nH
    75  }
    76  
    77  func (kdf *hkdfKDF) oneStage() bool {
    78  	return false
    79  }
    80  
    81  func (kdf *hkdfKDF) labeledDerive(_, _ []byte, _ string, _ []byte, _ uint16) ([]byte, error) {
    82  	return nil, errors.New("hpke: internal error: labeledDerive called on two-stage KDF")
    83  }
    84  
    85  func (kdf *hkdfKDF) labeledExtract(suiteID []byte, salt []byte, label string, inputKey []byte) ([]byte, error) {
    86  	labeledIKM := make([]byte, 0, 7+len(suiteID)+len(label)+len(inputKey))
    87  	labeledIKM = append(labeledIKM, []byte("HPKE-v1")...)
    88  	labeledIKM = append(labeledIKM, suiteID...)
    89  	labeledIKM = append(labeledIKM, label...)
    90  	labeledIKM = append(labeledIKM, inputKey...)
    91  	return hkdf.Extract(kdf.hash, labeledIKM, salt)
    92  }
    93  
    94  func (kdf *hkdfKDF) labeledExpand(suiteID []byte, randomKey []byte, label string, info []byte, length uint16) ([]byte, error) {
    95  	labeledInfo := make([]byte, 0, 2+7+len(suiteID)+len(label)+len(info))
    96  	labeledInfo = byteorder.BEAppendUint16(labeledInfo, length)
    97  	labeledInfo = append(labeledInfo, []byte("HPKE-v1")...)
    98  	labeledInfo = append(labeledInfo, suiteID...)
    99  	labeledInfo = append(labeledInfo, label...)
   100  	labeledInfo = append(labeledInfo, info...)
   101  	return hkdf.Expand(kdf.hash, randomKey, string(labeledInfo), int(length))
   102  }
   103  
   104  // SHAKE128 returns a SHAKE128 KDF implementation.
   105  func SHAKE128() KDF {
   106  	return shake128KDF
   107  }
   108  
   109  // SHAKE256 returns a SHAKE256 KDF implementation.
   110  func SHAKE256() KDF {
   111  	return shake256KDF
   112  }
   113  
   114  type shakeKDF struct {
   115  	hash func() *sha3.SHAKE
   116  	id   uint16
   117  	nH   int
   118  }
   119  
   120  var shake128KDF = &shakeKDF{hash: sha3.NewSHAKE128, id: 0x0010, nH: 32}
   121  var shake256KDF = &shakeKDF{hash: sha3.NewSHAKE256, id: 0x0011, nH: 64}
   122  
   123  func (kdf *shakeKDF) ID() uint16 {
   124  	return kdf.id
   125  }
   126  
   127  func (kdf *shakeKDF) size() int {
   128  	return kdf.nH
   129  }
   130  
   131  func (kdf *shakeKDF) oneStage() bool {
   132  	return true
   133  }
   134  
   135  func (kdf *shakeKDF) labeledDerive(suiteID, inputKey []byte, label string, context []byte, length uint16) ([]byte, error) {
   136  	H := kdf.hash()
   137  	H.Write(inputKey)
   138  	H.Write([]byte("HPKE-v1"))
   139  	H.Write(suiteID)
   140  	H.Write([]byte{byte(len(label) >> 8), byte(len(label))})
   141  	H.Write([]byte(label))
   142  	H.Write([]byte{byte(length >> 8), byte(length)})
   143  	H.Write(context)
   144  	out := make([]byte, length)
   145  	H.Read(out)
   146  	return out, nil
   147  }
   148  
   149  func (kdf *shakeKDF) labeledExtract(_, _ []byte, _ string, _ []byte) ([]byte, error) {
   150  	return nil, errors.New("hpke: internal error: labeledExtract called on one-stage KDF")
   151  }
   152  
   153  func (kdf *shakeKDF) labeledExpand(_, _ []byte, _ string, _ []byte, _ uint16) ([]byte, error) {
   154  	return nil, errors.New("hpke: internal error: labeledExpand called on one-stage KDF")
   155  }
   156  

View as plain text