github.com/cloudflare/circl@v1.5.0/hpke/algs.go (about)

     1  package hpke
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/aes"
     6  	"crypto/cipher"
     7  	"crypto/elliptic"
     8  	_ "crypto/sha256" // Linking sha256.
     9  	_ "crypto/sha512" // Linking sha512.
    10  	"fmt"
    11  	"hash"
    12  	"io"
    13  
    14  	"github.com/cloudflare/circl/dh/x25519"
    15  	"github.com/cloudflare/circl/dh/x448"
    16  	"github.com/cloudflare/circl/ecc/p384"
    17  	"github.com/cloudflare/circl/kem"
    18  	"github.com/cloudflare/circl/kem/kyber/kyber768"
    19  	"golang.org/x/crypto/chacha20poly1305"
    20  	"golang.org/x/crypto/hkdf"
    21  )
    22  
    23  type KEM uint16
    24  
    25  //nolint:golint,stylecheck
    26  const (
    27  	// KEM_P256_HKDF_SHA256 is a KEM using P256 curve and HKDF with SHA-256.
    28  	KEM_P256_HKDF_SHA256 KEM = 0x10
    29  	// KEM_P384_HKDF_SHA384 is a KEM using P384 curve and HKDF with SHA-384.
    30  	KEM_P384_HKDF_SHA384 KEM = 0x11
    31  	// KEM_P521_HKDF_SHA512 is a KEM using P521 curve and HKDF with SHA-512.
    32  	KEM_P521_HKDF_SHA512 KEM = 0x12
    33  	// KEM_X25519_HKDF_SHA256 is a KEM using X25519 Diffie-Hellman function
    34  	// and HKDF with SHA-256.
    35  	KEM_X25519_HKDF_SHA256 KEM = 0x20
    36  	// KEM_X448_HKDF_SHA512 is a KEM using X448 Diffie-Hellman function and
    37  	// HKDF with SHA-512.
    38  	KEM_X448_HKDF_SHA512 KEM = 0x21
    39  	// KEM_X25519_KYBER768_DRAFT00 is a hybrid KEM built on DHKEM(X25519, HKDF-SHA256)
    40  	// and Kyber768Draft00
    41  	KEM_X25519_KYBER768_DRAFT00 KEM = 0x30
    42  )
    43  
    44  // IsValid returns true if the KEM identifier is supported by the HPKE package.
    45  func (k KEM) IsValid() bool {
    46  	switch k {
    47  	case KEM_P256_HKDF_SHA256,
    48  		KEM_P384_HKDF_SHA384,
    49  		KEM_P521_HKDF_SHA512,
    50  		KEM_X25519_HKDF_SHA256,
    51  		KEM_X448_HKDF_SHA512,
    52  		KEM_X25519_KYBER768_DRAFT00:
    53  		return true
    54  	default:
    55  		return false
    56  	}
    57  }
    58  
    59  // Scheme returns an instance of a KEM that supports authentication. Panics if
    60  // the KEM identifier is invalid.
    61  func (k KEM) Scheme() kem.AuthScheme {
    62  	switch k {
    63  	case KEM_P256_HKDF_SHA256:
    64  		return dhkemp256hkdfsha256
    65  	case KEM_P384_HKDF_SHA384:
    66  		return dhkemp384hkdfsha384
    67  	case KEM_P521_HKDF_SHA512:
    68  		return dhkemp521hkdfsha512
    69  	case KEM_X25519_HKDF_SHA256:
    70  		return dhkemx25519hkdfsha256
    71  	case KEM_X448_HKDF_SHA512:
    72  		return dhkemx448hkdfsha512
    73  	case KEM_X25519_KYBER768_DRAFT00:
    74  		return hybridkemX25519Kyber768
    75  	default:
    76  		panic(ErrInvalidKEM)
    77  	}
    78  }
    79  
    80  type KDF uint16
    81  
    82  //nolint:golint,stylecheck
    83  const (
    84  	// KDF_HKDF_SHA256 is a KDF using HKDF with SHA-256.
    85  	KDF_HKDF_SHA256 KDF = 0x01
    86  	// KDF_HKDF_SHA384 is a KDF using HKDF with SHA-384.
    87  	KDF_HKDF_SHA384 KDF = 0x02
    88  	// KDF_HKDF_SHA512 is a KDF using HKDF with SHA-512.
    89  	KDF_HKDF_SHA512 KDF = 0x03
    90  )
    91  
    92  func (k KDF) IsValid() bool {
    93  	switch k {
    94  	case KDF_HKDF_SHA256,
    95  		KDF_HKDF_SHA384,
    96  		KDF_HKDF_SHA512:
    97  		return true
    98  	default:
    99  		return false
   100  	}
   101  }
   102  
   103  // ExtractSize returns the size (in bytes) of the pseudorandom key produced
   104  // by KDF.Extract.
   105  func (k KDF) ExtractSize() int {
   106  	switch k {
   107  	case KDF_HKDF_SHA256:
   108  		return crypto.SHA256.Size()
   109  	case KDF_HKDF_SHA384:
   110  		return crypto.SHA384.Size()
   111  	case KDF_HKDF_SHA512:
   112  		return crypto.SHA512.Size()
   113  	default:
   114  		panic(ErrInvalidKDF)
   115  	}
   116  }
   117  
   118  // Extract derives a pseudorandom key from a high-entropy, secret input and a
   119  // salt. The size of the output is determined by KDF.ExtractSize.
   120  func (k KDF) Extract(secret, salt []byte) (pseudorandomKey []byte) {
   121  	return hkdf.Extract(k.hash(), secret, salt)
   122  }
   123  
   124  // Expand derives a variable length pseudorandom string from a pseudorandom key
   125  // and an information string. Panics if the pseudorandom key is less
   126  // than N bytes, or if the output length is greater than 255*N bytes,
   127  // where N is the size returned by KDF.Extract function.
   128  func (k KDF) Expand(pseudorandomKey, info []byte, outputLen uint) []byte {
   129  	extractSize := k.ExtractSize()
   130  	if len(pseudorandomKey) < extractSize {
   131  		panic(fmt.Errorf("pseudorandom key must be %v bytes", extractSize))
   132  	}
   133  	maxLength := uint(255 * extractSize)
   134  	if outputLen > maxLength {
   135  		panic(fmt.Errorf("output length must be less than %v bytes", maxLength))
   136  	}
   137  	output := make([]byte, outputLen)
   138  	rd := hkdf.Expand(k.hash(), pseudorandomKey[:extractSize], info)
   139  	_, err := io.ReadFull(rd, output)
   140  	if err != nil {
   141  		panic(err)
   142  	}
   143  	return output
   144  }
   145  
   146  func (k KDF) hash() func() hash.Hash {
   147  	switch k {
   148  	case KDF_HKDF_SHA256:
   149  		return crypto.SHA256.New
   150  	case KDF_HKDF_SHA384:
   151  		return crypto.SHA384.New
   152  	case KDF_HKDF_SHA512:
   153  		return crypto.SHA512.New
   154  	default:
   155  		panic(ErrInvalidKDF)
   156  	}
   157  }
   158  
   159  type AEAD uint16
   160  
   161  //nolint:golint,stylecheck
   162  const (
   163  	// AEAD_AES128GCM is AES-128 block cipher in Galois Counter Mode (GCM).
   164  	AEAD_AES128GCM AEAD = 0x01
   165  	// AEAD_AES256GCM is AES-256 block cipher in Galois Counter Mode (GCM).
   166  	AEAD_AES256GCM AEAD = 0x02
   167  	// AEAD_ChaCha20Poly1305 is ChaCha20 stream cipher and Poly1305 MAC.
   168  	AEAD_ChaCha20Poly1305 AEAD = 0x03
   169  )
   170  
   171  // New instantiates an AEAD cipher from the identifier, returns an error if the
   172  // identifier is not known.
   173  func (a AEAD) New(key []byte) (cipher.AEAD, error) {
   174  	switch a {
   175  	case AEAD_AES128GCM, AEAD_AES256GCM:
   176  		block, err := aes.NewCipher(key)
   177  		if err != nil {
   178  			return nil, err
   179  		}
   180  		return cipher.NewGCM(block)
   181  	case AEAD_ChaCha20Poly1305:
   182  		return chacha20poly1305.New(key)
   183  	default:
   184  		panic(ErrInvalidAEAD)
   185  	}
   186  }
   187  
   188  func (a AEAD) IsValid() bool {
   189  	switch a {
   190  	case AEAD_AES128GCM,
   191  		AEAD_AES256GCM,
   192  		AEAD_ChaCha20Poly1305:
   193  		return true
   194  	default:
   195  		return false
   196  	}
   197  }
   198  
   199  // KeySize returns the size in bytes of the keys used by the AEAD cipher.
   200  func (a AEAD) KeySize() uint {
   201  	switch a {
   202  	case AEAD_AES128GCM:
   203  		return 16
   204  	case AEAD_AES256GCM:
   205  		return 32
   206  	case AEAD_ChaCha20Poly1305:
   207  		return chacha20poly1305.KeySize
   208  	default:
   209  		panic(ErrInvalidAEAD)
   210  	}
   211  }
   212  
   213  // NonceSize returns the size in bytes of the nonce used by the AEAD cipher.
   214  func (a AEAD) NonceSize() uint {
   215  	switch a {
   216  	case AEAD_AES128GCM,
   217  		AEAD_AES256GCM,
   218  		AEAD_ChaCha20Poly1305:
   219  		return 12
   220  	default:
   221  		panic(ErrInvalidAEAD)
   222  	}
   223  }
   224  
   225  // CipherLen returns the length of a ciphertext corresponding to a message of
   226  // length mLen.
   227  func (a AEAD) CipherLen(mLen uint) uint {
   228  	switch a {
   229  	case AEAD_AES128GCM, AEAD_AES256GCM, AEAD_ChaCha20Poly1305:
   230  		return mLen + 16
   231  	default:
   232  		panic(ErrInvalidAEAD)
   233  	}
   234  }
   235  
   236  var (
   237  	dhkemp256hkdfsha256, dhkemp384hkdfsha384, dhkemp521hkdfsha512 shortKEM
   238  	dhkemx25519hkdfsha256, dhkemx448hkdfsha512                    xKEM
   239  	hybridkemX25519Kyber768                                       hybridKEM
   240  )
   241  
   242  func init() {
   243  	dhkemp256hkdfsha256.Curve = elliptic.P256()
   244  	dhkemp256hkdfsha256.dhKemBase.id = KEM_P256_HKDF_SHA256
   245  	dhkemp256hkdfsha256.dhKemBase.name = "HPKE_KEM_P256_HKDF_SHA256"
   246  	dhkemp256hkdfsha256.dhKemBase.Hash = crypto.SHA256
   247  	dhkemp256hkdfsha256.dhKemBase.dhKEM = dhkemp256hkdfsha256
   248  
   249  	dhkemp384hkdfsha384.Curve = p384.P384()
   250  	dhkemp384hkdfsha384.dhKemBase.id = KEM_P384_HKDF_SHA384
   251  	dhkemp384hkdfsha384.dhKemBase.name = "HPKE_KEM_P384_HKDF_SHA384"
   252  	dhkemp384hkdfsha384.dhKemBase.Hash = crypto.SHA384
   253  	dhkemp384hkdfsha384.dhKemBase.dhKEM = dhkemp384hkdfsha384
   254  
   255  	dhkemp521hkdfsha512.Curve = elliptic.P521()
   256  	dhkemp521hkdfsha512.dhKemBase.id = KEM_P521_HKDF_SHA512
   257  	dhkemp521hkdfsha512.dhKemBase.name = "HPKE_KEM_P521_HKDF_SHA512"
   258  	dhkemp521hkdfsha512.dhKemBase.Hash = crypto.SHA512
   259  	dhkemp521hkdfsha512.dhKemBase.dhKEM = dhkemp521hkdfsha512
   260  
   261  	dhkemx25519hkdfsha256.size = x25519.Size
   262  	dhkemx25519hkdfsha256.dhKemBase.id = KEM_X25519_HKDF_SHA256
   263  	dhkemx25519hkdfsha256.dhKemBase.name = "HPKE_KEM_X25519_HKDF_SHA256"
   264  	dhkemx25519hkdfsha256.dhKemBase.Hash = crypto.SHA256
   265  	dhkemx25519hkdfsha256.dhKemBase.dhKEM = dhkemx25519hkdfsha256
   266  
   267  	dhkemx448hkdfsha512.size = x448.Size
   268  	dhkemx448hkdfsha512.dhKemBase.id = KEM_X448_HKDF_SHA512
   269  	dhkemx448hkdfsha512.dhKemBase.name = "HPKE_KEM_X448_HKDF_SHA512"
   270  	dhkemx448hkdfsha512.dhKemBase.Hash = crypto.SHA512
   271  	dhkemx448hkdfsha512.dhKemBase.dhKEM = dhkemx448hkdfsha512
   272  
   273  	hybridkemX25519Kyber768.kemBase.id = KEM_X25519_KYBER768_DRAFT00
   274  	hybridkemX25519Kyber768.kemBase.name = "HPKE_KEM_X25519_KYBER768_HKDF_SHA256"
   275  	hybridkemX25519Kyber768.kemBase.Hash = crypto.SHA256
   276  	hybridkemX25519Kyber768.kemA = dhkemx25519hkdfsha256
   277  	hybridkemX25519Kyber768.kemB = kyber768.Scheme()
   278  }