patterns.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package handshake
  2. import (
  3. "fmt"
  4. "github.com/DefinedNet/noise"
  5. "github.com/slackhq/nebula/cert"
  6. "github.com/slackhq/nebula/header"
  7. )
  8. // msgFlags tracks what application data a handshake message carries.
  9. type msgFlags struct {
  10. expectsPayload bool // message carries indexes and time
  11. expectsCert bool // message carries the certificate
  12. }
  13. // subtypeInfo bundles the noise pattern with the per-message flags for a
  14. // given handshake subtype.
  15. type subtypeInfo struct {
  16. pattern noise.HandshakePattern
  17. msgs []msgFlags
  18. // kem is the KEM of a hybrid pattern, nil for any other. Hybrid patterns
  19. // carry composite static keys and need version 3 certificates on both
  20. // sides, in suites using this KEM; every other pattern refuses them.
  21. kem noise.KEMFunc
  22. }
  23. // subtypeInfos defines the noise pattern and message content layout for each
  24. // handshake subtype.
  25. var subtypeInfos = map[header.MessageSubType]subtypeInfo{
  26. // IX: 2 messages, both carry payload and cert
  27. header.HandshakeIXPSK0: {
  28. pattern: noise.HandshakeIX,
  29. msgs: []msgFlags{
  30. {expectsPayload: true, expectsCert: true},
  31. {expectsPayload: true, expectsCert: true},
  32. },
  33. },
  34. // Hybrid IX (github.com/DefinedNet/noise, see its PQ_RESEARCH.md):
  35. // msg1 (I->R): e, s payload + cert
  36. // msg2 (R->I): ekem, skem, e, ee, se, s, es payload + cert
  37. // msg3 (I->R): skem
  38. // e and s are composite, classical || ML-KEM-1024, except the responder's e:
  39. // it follows the responder's ekem, which already completed the ephemeral KEM
  40. // exchange, so it carries only the classical key. The static ML-KEM key is
  41. // the post-quantum half of the v3 certificate's public key, so it travels in
  42. // s and the certificate leaves it out. The responder authenticates the
  43. // initiator when msg3 decrypts, and the initiator the responder when the
  44. // first packet under the new keys does.
  45. header.HandshakeIXHybridMLKEM1024: {
  46. pattern: noise.HandshakeHybridIX,
  47. kem: KEMMLKEM1024,
  48. msgs: []msgFlags{
  49. {expectsPayload: true, expectsCert: true},
  50. {expectsPayload: true, expectsCert: true},
  51. {},
  52. },
  53. },
  54. // XX: 3 messages
  55. // msg1 (I->R): payload only
  56. // msg2 (R->I): payload + cert
  57. // msg3 (I->R): cert only
  58. //header.HandshakeXXPSK0: {
  59. // pattern: noise.HandshakeXX,
  60. // msgs: []msgFlags{
  61. // {expectsPayload: true, expectsCert: false},
  62. // {expectsPayload: true, expectsCert: true},
  63. // {expectsPayload: false, expectsCert: true},
  64. // },
  65. //},
  66. }
  67. // hybridSubtypes maps the suite of a version 3 certificate to the hybrid
  68. // subtype for its KEM. Peers need the same KEM, not the same suite, so suites
  69. // sharing a KEM share a subtype; the suite itself travels in the certificate.
  70. var hybridSubtypes = map[cert.PQSuite]header.MessageSubType{
  71. cert.PQSuite_MLKEM1024_MLDSA87: header.HandshakeIXHybridMLKEM1024,
  72. }
  73. // HybridSubtype returns the hybrid handshake subtype for the KEM of a version 3
  74. // certificate's suite.
  75. func HybridSubtype(suite cert.PQSuite) (header.MessageSubType, bool) {
  76. st, ok := hybridSubtypes[suite]
  77. return st, ok
  78. }
  79. // IsHybrid reports whether subtype is a hybrid handshake, one that needs version
  80. // 3 certificates.
  81. func IsHybrid(subtype header.MessageSubType) bool {
  82. return subtypeInfos[subtype].kem != nil
  83. }
  84. func subtypeInfoFor(subtype header.MessageSubType) (subtypeInfo, error) {
  85. if info, ok := subtypeInfos[subtype]; ok {
  86. return info, nil
  87. }
  88. return subtypeInfo{}, fmt.Errorf("%w: %d", ErrUnknownSubtype, subtype)
  89. }