Source file src/crypto/hpke/aead.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/aes"
     9  	"crypto/cipher"
    10  	"errors"
    11  	"fmt"
    12  
    13  	"golang.org/x/crypto/chacha20poly1305"
    14  )
    15  
    16  // The AEAD is one of the three components of an HPKE ciphersuite, implementing
    17  // symmetric encryption.
    18  type AEAD interface {
    19  	ID() uint16
    20  	keySize() int
    21  	nonceSize() int
    22  	aead(key []byte) (cipher.AEAD, error)
    23  }
    24  
    25  // NewAEAD returns the AEAD implementation for the given AEAD ID.
    26  //
    27  // Applications are encouraged to use specific implementations like [AES128GCM]
    28  // or [ChaCha20Poly1305] instead, unless runtime agility is required.
    29  func NewAEAD(id uint16) (AEAD, error) {
    30  	switch id {
    31  	case 0x0001: // AES-128-GCM
    32  		return AES128GCM(), nil
    33  	case 0x0002: // AES-256-GCM
    34  		return AES256GCM(), nil
    35  	case 0x0003: // ChaCha20Poly1305
    36  		return ChaCha20Poly1305(), nil
    37  	case 0xFFFF: // Export-only
    38  		return ExportOnly(), nil
    39  	default:
    40  		return nil, fmt.Errorf("unsupported AEAD %04x", id)
    41  	}
    42  }
    43  
    44  // AES128GCM returns an AES-128-GCM AEAD implementation.
    45  func AES128GCM() AEAD { return aes128GCM }
    46  
    47  // AES256GCM returns an AES-256-GCM AEAD implementation.
    48  func AES256GCM() AEAD { return aes256GCM }
    49  
    50  // ChaCha20Poly1305 returns a ChaCha20Poly1305 AEAD implementation.
    51  func ChaCha20Poly1305() AEAD { return chacha20poly1305AEAD }
    52  
    53  // ExportOnly returns a placeholder AEAD implementation that cannot encrypt or
    54  // decrypt, but only export secrets with [Sender.Export] or [Recipient.Export].
    55  //
    56  // When this is used, [Sender.Seal] and [Recipient.Open] return errors.
    57  func ExportOnly() AEAD { return exportOnlyAEAD{} }
    58  
    59  type aead struct {
    60  	nK  int
    61  	nN  int
    62  	new func([]byte) (cipher.AEAD, error)
    63  	id  uint16
    64  }
    65  
    66  var aes128GCM = &aead{
    67  	nK:  128 / 8,
    68  	nN:  96 / 8,
    69  	new: newAESGCM,
    70  	id:  0x0001,
    71  }
    72  
    73  var aes256GCM = &aead{
    74  	nK:  256 / 8,
    75  	nN:  96 / 8,
    76  	new: newAESGCM,
    77  	id:  0x0002,
    78  }
    79  
    80  var chacha20poly1305AEAD = &aead{
    81  	nK:  chacha20poly1305.KeySize,
    82  	nN:  chacha20poly1305.NonceSize,
    83  	new: chacha20poly1305.New,
    84  	id:  0x0003,
    85  }
    86  
    87  func newAESGCM(key []byte) (cipher.AEAD, error) {
    88  	b, err := aes.NewCipher(key)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	return cipher.NewGCM(b)
    93  }
    94  
    95  func (a *aead) ID() uint16 {
    96  	return a.id
    97  }
    98  
    99  func (a *aead) aead(key []byte) (cipher.AEAD, error) {
   100  	if len(key) != a.nK {
   101  		return nil, errors.New("invalid key size")
   102  	}
   103  	return a.new(key)
   104  }
   105  
   106  func (a *aead) keySize() int {
   107  	return a.nK
   108  }
   109  
   110  func (a *aead) nonceSize() int {
   111  	return a.nN
   112  }
   113  
   114  type exportOnlyAEAD struct{}
   115  
   116  func (exportOnlyAEAD) ID() uint16 {
   117  	return 0xFFFF
   118  }
   119  
   120  func (exportOnlyAEAD) aead(key []byte) (cipher.AEAD, error) {
   121  	return nil, nil
   122  }
   123  
   124  func (exportOnlyAEAD) keySize() int {
   125  	return 0
   126  }
   127  
   128  func (exportOnlyAEAD) nonceSize() int {
   129  	return 0
   130  }
   131  

View as plain text