mirror of
https://github.com/slackhq/nebula.git
synced 2026-11-15 13:56:52 +01:00
102 lines
3.4 KiB
Go
102 lines
3.4 KiB
Go
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)
|
|
}
|