algs.go 8.0 KB

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