algs.go 7.7 KB

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