cfkem.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // Copyright 2022 Cloudflare, Inc. All rights reserved. Use of this source code
  2. // is governed by a BSD-style license that can be found in the LICENSE file.
  3. //
  4. // Glue to add Circl's (post-quantum) hybrid KEMs.
  5. //
  6. // To enable set CurvePreferences with the desired scheme as the first element:
  7. //
  8. // import (
  9. // "crypto/tls"
  10. //
  11. // [...]
  12. //
  13. // config.CurvePreferences = []tls.CurveID{
  14. // tls.X25519Kyber768Draft00,
  15. // tls.X25519,
  16. // tls.P256,
  17. // }
  18. package tls
  19. import (
  20. "fmt"
  21. "io"
  22. "crypto/ecdh"
  23. "github.com/cloudflare/circl/kem"
  24. "github.com/cloudflare/circl/kem/hybrid"
  25. )
  26. // Either *ecdh.PrivateKey or *kemPrivateKey
  27. type clientKeySharePrivate interface{}
  28. type kemPrivateKey struct {
  29. secretKey kem.PrivateKey
  30. curveID CurveID
  31. }
  32. var (
  33. X25519Kyber512Draft00 = CurveID(0xfe30)
  34. X25519Kyber768Draft00 = CurveID(0x6399)
  35. X25519Kyber768Draft00Old = CurveID(0xfe31)
  36. P256Kyber768Draft00 = CurveID(0xfe32)
  37. invalidCurveID = CurveID(0)
  38. )
  39. // Extract CurveID from clientKeySharePrivate
  40. func clientKeySharePrivateCurveID(ks clientKeySharePrivate) CurveID {
  41. switch v := ks.(type) {
  42. case *kemPrivateKey:
  43. return v.curveID
  44. case *ecdh.PrivateKey:
  45. ret, ok := curveIDForCurve(v.Curve())
  46. if !ok {
  47. panic("cfkem: internal error: unknown curve")
  48. }
  49. return ret
  50. default:
  51. panic("cfkem: internal error: unknown clientKeySharePrivate")
  52. }
  53. }
  54. // Returns scheme by CurveID if supported by Circl
  55. func curveIdToCirclScheme(id CurveID) kem.Scheme {
  56. switch id {
  57. case X25519Kyber512Draft00:
  58. return hybrid.Kyber512X25519()
  59. case X25519Kyber768Draft00, X25519Kyber768Draft00Old:
  60. return hybrid.Kyber768X25519()
  61. case P256Kyber768Draft00:
  62. return hybrid.P256Kyber768Draft00()
  63. }
  64. return nil
  65. }
  66. // Generate a new shared secret and encapsulates it for the packed
  67. // public key in ppk using randomness from rnd.
  68. func encapsulateForKem(scheme kem.Scheme, rnd io.Reader, ppk []byte) (
  69. ct, ss []byte, alert alert, err error) {
  70. pk, err := scheme.UnmarshalBinaryPublicKey(ppk)
  71. if err != nil {
  72. return nil, nil, alertIllegalParameter, fmt.Errorf("unpack pk: %w", err)
  73. }
  74. seed := make([]byte, scheme.EncapsulationSeedSize())
  75. if _, err := io.ReadFull(rnd, seed); err != nil {
  76. return nil, nil, alertInternalError, fmt.Errorf("random: %w", err)
  77. }
  78. ct, ss, err = scheme.EncapsulateDeterministically(pk, seed)
  79. return ct, ss, alertIllegalParameter, err
  80. }
  81. // Generate a new keypair using randomness from rnd.
  82. func generateKemKeyPair(scheme kem.Scheme, curveID CurveID, rnd io.Reader) (
  83. kem.PublicKey, *kemPrivateKey, error) {
  84. seed := make([]byte, scheme.SeedSize())
  85. if _, err := io.ReadFull(rnd, seed); err != nil {
  86. return nil, nil, err
  87. }
  88. pk, sk := scheme.DeriveKeyPair(seed)
  89. return pk, &kemPrivateKey{sk, curveID}, nil
  90. }