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 }