| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- package handshake
- import (
- "fmt"
- "github.com/DefinedNet/noise"
- "github.com/slackhq/nebula/cert"
- "github.com/slackhq/nebula/header"
- )
- // msgFlags tracks what application data a handshake message carries.
- type msgFlags struct {
- expectsPayload bool // message carries indexes and time
- expectsCert bool // message carries the certificate
- }
- // subtypeInfo bundles the noise pattern with the per-message flags for a
- // given handshake subtype.
- type subtypeInfo struct {
- pattern noise.HandshakePattern
- msgs []msgFlags
- // kem is the KEM of a hybrid pattern, nil for any other. Hybrid patterns
- // carry composite static keys and need version 3 certificates on both
- // sides, in suites using this KEM; every other pattern refuses them.
- kem noise.KEMFunc
- }
- // subtypeInfos defines the noise pattern and message content layout for each
- // handshake subtype.
- var subtypeInfos = map[header.MessageSubType]subtypeInfo{
- // IX: 2 messages, both carry payload and cert
- header.HandshakeIXPSK0: {
- pattern: noise.HandshakeIX,
- msgs: []msgFlags{
- {expectsPayload: true, expectsCert: true},
- {expectsPayload: true, expectsCert: true},
- },
- },
- // Hybrid IX (github.com/DefinedNet/noise, see its PQ_RESEARCH.md):
- // msg1 (I->R): e, s payload + cert
- // msg2 (R->I): ekem, skem, e, ee, se, s, es payload + cert
- // msg3 (I->R): skem
- // e and s are composite, classical || ML-KEM-1024, except the responder's e:
- // it follows the responder's ekem, which already completed the ephemeral KEM
- // exchange, so it carries only the classical key. The static ML-KEM key is
- // the post-quantum half of the v3 certificate's public key, so it travels in
- // s and the certificate leaves it out. The responder authenticates the
- // initiator when msg3 decrypts, and the initiator the responder when the
- // first packet under the new keys does.
- header.HandshakeIXHybridMLKEM1024: {
- pattern: noise.HandshakeHybridIX,
- kem: KEMMLKEM1024,
- msgs: []msgFlags{
- {expectsPayload: true, expectsCert: true},
- {expectsPayload: true, expectsCert: true},
- {},
- },
- },
- // XX: 3 messages
- // msg1 (I->R): payload only
- // msg2 (R->I): payload + cert
- // msg3 (I->R): cert only
- //header.HandshakeXXPSK0: {
- // pattern: noise.HandshakeXX,
- // msgs: []msgFlags{
- // {expectsPayload: true, expectsCert: false},
- // {expectsPayload: true, expectsCert: true},
- // {expectsPayload: false, expectsCert: true},
- // },
- //},
- }
- // hybridSubtypes maps the suite of a version 3 certificate to the hybrid
- // subtype for its KEM. Peers need the same KEM, not the same suite, so suites
- // sharing a KEM share a subtype; the suite itself travels in the certificate.
- var hybridSubtypes = map[cert.PQSuite]header.MessageSubType{
- cert.PQSuite_MLKEM1024_MLDSA87: header.HandshakeIXHybridMLKEM1024,
- }
- // HybridSubtype returns the hybrid handshake subtype for the KEM of a version 3
- // certificate's suite.
- func HybridSubtype(suite cert.PQSuite) (header.MessageSubType, bool) {
- st, ok := hybridSubtypes[suite]
- return st, ok
- }
- // IsHybrid reports whether subtype is a hybrid handshake, one that needs version
- // 3 certificates.
- func IsHybrid(subtype header.MessageSubType) bool {
- return subtypeInfos[subtype].kem != nil
- }
- func subtypeInfoFor(subtype header.MessageSubType) (subtypeInfo, error) {
- if info, ok := subtypeInfos[subtype]; ok {
- return info, nil
- }
- return subtypeInfo{}, fmt.Errorf("%w: %d", ErrUnknownSubtype, subtype)
- }
|