credential.go 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package handshake
  2. import (
  3. "crypto/mlkem"
  4. "crypto/rand"
  5. "github.com/DefinedNet/noise"
  6. "github.com/slackhq/nebula/cert"
  7. )
  8. // Credential holds everything needed to participate in a handshake
  9. // at a given cert version. Version and Curve are read from Cert; the public
  10. // half of the static keypair likewise comes from Cert.PublicKey().
  11. type Credential struct {
  12. Cert cert.Certificate // the certificate
  13. Bytes []byte // pre-marshaled certificate bytes
  14. privateKey []byte // static private key (public half lives in Cert)
  15. publicKey []byte // static public key, the classical half for a v3 Cert
  16. kemKey noise.KEMKey // static KEM keypair of the Cert's suite, only for a v3 Cert
  17. kemDK *mlkem.DecapsulationKey1024 // kemKey expanded, when the suite's KEM is ML-KEM-1024
  18. cipherSuite noise.CipherSuite // pre-built cipher suite (DH + cipher + hash)
  19. }
  20. // NewCredential creates a Credential with all material needed for handshake
  21. // participation. The cipherSuite should be pre-built by the caller with the
  22. // appropriate DH function, cipher, and hash.
  23. //
  24. // For a version 3 certificate privateKey is the composite private key,
  25. // classical || the seed of its suite's KEM, matching the composite
  26. // Cert.PublicKey().
  27. func NewCredential(
  28. c cert.Certificate,
  29. hsBytes []byte,
  30. privateKey []byte,
  31. cipherSuite noise.CipherSuite,
  32. ) *Credential {
  33. hc := &Credential{
  34. Cert: c,
  35. Bytes: hsBytes,
  36. privateKey: privateKey,
  37. publicKey: c.PublicKey(),
  38. cipherSuite: cipherSuite,
  39. }
  40. if suite := cert.PQSuiteOf(c); suite != cert.PQSuiteUnknown {
  41. // The pki checked the private key against the certificate, so both split.
  42. hc.publicKey, hc.kemKey.Public, _ = suite.SplitPublicKey(false, c.PublicKey())
  43. hc.privateKey, hc.kemKey.Private, _ = suite.SplitPrivateKey(false, privateKey)
  44. // Expand the static KEM key once rather than in every handshake.
  45. if st, ok := HybridSubtype(suite); ok && subtypeInfos[st].kem == KEMMLKEM1024 {
  46. hc.kemDK, _ = mlkem.NewDecapsulationKey1024(hc.kemKey.Private)
  47. }
  48. }
  49. return hc
  50. }
  51. // buildHandshakeState creates a noise.HandshakeState from this credential. A
  52. // hybrid pattern also gets its KEM and the static KEM key.
  53. func (hc *Credential) buildHandshakeState(initiator bool, info subtypeInfo) (*noise.HandshakeState, error) {
  54. cfg := noise.Config{
  55. CipherSuite: hc.cipherSuite,
  56. Random: rand.Reader,
  57. Pattern: info.pattern,
  58. Initiator: initiator,
  59. StaticKeypair: noise.DHKey{Private: hc.privateKey, Public: hc.publicKey},
  60. }
  61. if info.kem != nil {
  62. cfg.KEMFunc = info.kem
  63. cfg.StaticKEMKeypair = hc.kemKey
  64. if info.kem == KEMMLKEM1024 {
  65. cfg.KEMFunc = &mlkem1024Keys{static: hc.kemDK, staticSeed: hc.kemKey.Private}
  66. }
  67. }
  68. return noise.NewHandshakeState(cfg)
  69. }
  70. // GetCredentialFunc returns the handshake credential for the given version,
  71. // or nil if that version is not available.
  72. //
  73. // Implementations must return credentials drawn from a snapshot stable for
  74. // the lifetime of any single Machine. The Machine may call this multiple
  75. // times during a handshake (e.g. when negotiating to the peer's version)
  76. // and assumes the underlying static keypair is consistent across calls.
  77. type GetCredentialFunc func(v cert.Version) *Credential