| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- package ike
- import (
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "vocat/internal/vowifi"
- )
- const (
- configRequest = 1
- configReply = 2
- configInternalIPv4Address = 1
- configInternalIPv4DNS = 3
- configInternalIPv6Address = 8
- configInternalIPv6DNS = 10
- configPCSCFIPv4Address = 20
- configPCSCFIPv6Address = 21
- trafficSelectorIPv4Range = 7
- trafficSelectorIPv6Range = 8
- )
- type espSuite struct {
- EncryptionID uint16
- EncryptionBits int
- IntegrityID uint16
- ESN uint16
- }
- func (suite espSuite) encryptionKeyLength() (int, error) {
- if suite.EncryptionID != encryptionAESCBC || (suite.EncryptionBits != 128 && suite.EncryptionBits != 256) {
- return 0, fmt.Errorf("%w: ESP encryption id=%d bits=%d", errUnsupportedSuite, suite.EncryptionID, suite.EncryptionBits)
- }
- return suite.EncryptionBits / 8, nil
- }
- func (suite espSuite) integrityKeyLength() (int, error) {
- switch suite.IntegrityID {
- case integrityHMACSHA1_96:
- return 20, nil
- case integrityHMACSHA256_128:
- return 32, nil
- default:
- return 0, fmt.Errorf("%w: ESP integrity id=%d", errUnsupportedSuite, suite.IntegrityID)
- }
- }
- func parseESPSuite(item proposal) (espSuite, error) {
- if item.Protocol != protocolESP || len(item.SPI) != 4 {
- return espSuite{}, fmt.Errorf("%w: invalid ESP proposal protocol or SPI", errUnsupportedSuite)
- }
- var suite espSuite
- seen := make(map[uint8]bool)
- for _, candidate := range item.Transforms {
- if seen[candidate.Type] {
- return espSuite{}, fmt.Errorf("%w: duplicate ESP transform type %d", errUnsupportedSuite, candidate.Type)
- }
- seen[candidate.Type] = true
- switch candidate.Type {
- case transformEncryption:
- suite.EncryptionID = candidate.ID
- suite.EncryptionBits = candidate.KeyLength
- case transformIntegrity:
- suite.IntegrityID = candidate.ID
- case transformESN:
- suite.ESN = candidate.ID
- default:
- return espSuite{}, fmt.Errorf("%w: unsupported ESP transform type %d", errUnsupportedSuite, candidate.Type)
- }
- }
- if _, err := suite.encryptionKeyLength(); err != nil {
- return espSuite{}, err
- }
- if _, err := suite.integrityKeyLength(); err != nil {
- return espSuite{}, err
- }
- if suite.ESN != 0 {
- return espSuite{}, fmt.Errorf("%w: ESP extended sequence numbers are unsupported", errUnsupportedSuite)
- }
- return suite, nil
- }
- type trafficSelector struct {
- IPProtocol uint8
- StartPort uint16
- EndPort uint16
- StartIP net.IP
- EndIP net.IP
- }
- func anyTrafficSelector(ipv6 bool) payload {
- var selector []byte
- if ipv6 {
- selector = make([]byte, 40)
- selector[0] = trafficSelectorIPv6Range
- binary.BigEndian.PutUint16(selector[2:4], uint16(len(selector)))
- binary.BigEndian.PutUint16(selector[6:8], 65535)
- copy(selector[8:24], net.IPv6zero)
- for index := 24; index < 40; index++ {
- selector[index] = 0xff
- }
- } else {
- selector = make([]byte, 16)
- selector[0] = trafficSelectorIPv4Range
- binary.BigEndian.PutUint16(selector[2:4], uint16(len(selector)))
- binary.BigEndian.PutUint16(selector[6:8], 65535)
- copy(selector[8:12], net.IPv4zero.To4())
- copy(selector[12:16], net.IPv4bcast.To4())
- }
- body := append([]byte{1, 0, 0, 0}, selector...)
- return payload{Body: body}
- }
- func dualStackTrafficSelectors(kind uint8) payload {
- ipv4 := anyTrafficSelector(false)
- ipv6 := anyTrafficSelector(true)
- body := []byte{2, 0, 0, 0}
- body = append(body, ipv4.Body[4:]...)
- body = append(body, ipv6.Body[4:]...)
- return payload{Type: kind, Body: body}
- }
- func parseTrafficSelectors(item payload) ([]trafficSelector, error) {
- if len(item.Body) < 4 {
- return nil, errors.New("ike: traffic selector payload is truncated")
- }
- count := int(item.Body[0])
- offset := 4
- result := make([]trafficSelector, 0, count)
- for index := 0; index < count; index++ {
- if offset+8 > len(item.Body) {
- return nil, errors.New("ike: traffic selector is truncated")
- }
- length := int(binary.BigEndian.Uint16(item.Body[offset+2 : offset+4]))
- if length < 16 || offset+length > len(item.Body) {
- return nil, errors.New("ike: traffic selector has an invalid length")
- }
- selector := trafficSelector{
- IPProtocol: item.Body[offset+1],
- StartPort: binary.BigEndian.Uint16(item.Body[offset+4 : offset+6]),
- EndPort: binary.BigEndian.Uint16(item.Body[offset+6 : offset+8]),
- }
- switch item.Body[offset] {
- case trafficSelectorIPv4Range:
- if length != 16 {
- return nil, errors.New("ike: IPv4 traffic selector has an invalid length")
- }
- selector.StartIP = append(net.IP(nil), item.Body[offset+8:offset+12]...)
- selector.EndIP = append(net.IP(nil), item.Body[offset+12:offset+16]...)
- case trafficSelectorIPv6Range:
- if length != 40 {
- return nil, errors.New("ike: IPv6 traffic selector has an invalid length")
- }
- selector.StartIP = append(net.IP(nil), item.Body[offset+8:offset+24]...)
- selector.EndIP = append(net.IP(nil), item.Body[offset+24:offset+40]...)
- default:
- return nil, fmt.Errorf("ike: unsupported traffic selector type %d", item.Body[offset])
- }
- result = append(result, selector)
- offset += length
- }
- if offset != len(item.Body) {
- return nil, errors.New("ike: traffic selector payload has trailing bytes")
- }
- return result, nil
- }
- type networkConfiguration struct {
- LocalIPv4 net.IP
- LocalIPv6 net.IP
- IPv6Prefix uint8
- DNS []net.IP
- PCSCF []net.IP
- }
- func configurationRequest() payload {
- attributes := []uint16{
- configInternalIPv4Address,
- configInternalIPv6Address,
- configInternalIPv4DNS,
- configInternalIPv6DNS,
- configPCSCFIPv4Address,
- configPCSCFIPv6Address,
- }
- body := []byte{configRequest, 0, 0, 0}
- for _, attribute := range attributes {
- var header [4]byte
- binary.BigEndian.PutUint16(header[0:2], attribute)
- body = append(body, header[:]...)
- }
- return payload{Type: payloadCP, Body: body}
- }
- func parseConfiguration(item payload) (networkConfiguration, error) {
- if item.Type != payloadCP || len(item.Body) < 4 || item.Body[0] != configReply {
- return networkConfiguration{}, errors.New("ike: missing or invalid configuration reply")
- }
- var configuration networkConfiguration
- for offset := 4; offset < len(item.Body); {
- if offset+4 > len(item.Body) {
- return networkConfiguration{}, errors.New("ike: truncated configuration attribute")
- }
- kind := binary.BigEndian.Uint16(item.Body[offset : offset+2])
- length := int(binary.BigEndian.Uint16(item.Body[offset+2 : offset+4]))
- offset += 4
- if offset+length > len(item.Body) {
- return networkConfiguration{}, errors.New("ike: invalid configuration attribute length")
- }
- value := item.Body[offset : offset+length]
- switch kind & 0x7fff {
- case configInternalIPv4Address:
- if length == 4 {
- configuration.LocalIPv4 = append(net.IP(nil), value...)
- }
- case configInternalIPv6Address:
- if length != 17 {
- return networkConfiguration{}, errors.New("ike: INTERNAL_IP6_ADDRESS must contain 16 address bytes and one prefix byte")
- }
- if value[16] > 128 {
- return networkConfiguration{}, errors.New("ike: INTERNAL_IP6_ADDRESS prefix exceeds 128")
- }
- configuration.LocalIPv6 = append(net.IP(nil), value[:16]...)
- configuration.IPv6Prefix = value[16]
- case configInternalIPv4DNS:
- if length == 4 {
- configuration.DNS = append(configuration.DNS, append(net.IP(nil), value...))
- }
- case configInternalIPv6DNS:
- if length == 16 {
- configuration.DNS = append(configuration.DNS, append(net.IP(nil), value...))
- }
- case configPCSCFIPv4Address:
- if length == 4 {
- configuration.PCSCF = append(configuration.PCSCF, append(net.IP(nil), value...))
- }
- case configPCSCFIPv6Address:
- if length == 16 {
- configuration.PCSCF = append(configuration.PCSCF, append(net.IP(nil), value...))
- }
- }
- offset += length
- }
- return configuration, nil
- }
- type ChildSAConfig struct {
- Name string
- OuterLocal net.IP
- OuterRemote net.IP
- InnerLocalIPv4 net.IP
- InnerLocalIPv6 net.IP
- InnerIPv6Prefix uint8
- PCSCF []net.IP
- DNS []net.IP
- InboundSPI uint32
- OutboundSPI uint32
- Encryption string
- Integrity string
- InboundEncKey []byte
- InboundAuthKey []byte
- OutboundEncKey []byte
- OutboundAuthKey []byte
- InitiatorSelectors []trafficSelector
- ResponderSelectors []trafficSelector
- UDPEncapsulation bool
- ProxyMode vowifi.ProxyMode
- Relay NATTPacketRelay
- }
- // NATTPacketRelay carries raw ESP packets inside UDP/4500. A user-space
- // CHILD_SA installer must use this relay when ProxyMode is SOCKS5; kernel
- // XFRM output cannot transparently enter a SOCKS5 UDP association.
- type NATTPacketRelay interface {
- SendESP(context.Context, []byte) error
- ReceiveESP(context.Context, []byte) (int, error)
- }
- type ChildSAHandle interface {
- Close(context.Context) error
- }
- type DataplaneEvidence interface {
- DataplaneMode() string
- }
- type DataplaneFailureNotifier interface {
- Failures() <-chan error
- }
- type ChildSAInstaller interface {
- Install(context.Context, ChildSAConfig) (ChildSAHandle, error)
- }
- func deriveChildSAKeys(
- ikeSuite negotiatedSuite,
- childSuite espSuite,
- skd []byte,
- initiatorNonce []byte,
- responderNonce []byte,
- ) (outboundEncryption, outboundIntegrity, inboundEncryption, inboundIntegrity []byte, err error) {
- encryptionLength, err := childSuite.encryptionKeyLength()
- if err != nil {
- return nil, nil, nil, nil, err
- }
- integrityLength, err := childSuite.integrityKeyLength()
- if err != nil {
- return nil, nil, nil, nil, err
- }
- seed := append(append([]byte(nil), initiatorNonce...), responderNonce...)
- stream, err := prfPlus(ikeSuite, skd, seed, 2*(encryptionLength+integrityLength))
- if err != nil {
- return nil, nil, nil, nil, err
- }
- take := func(length int) []byte {
- value := append([]byte(nil), stream[:length]...)
- stream = stream[length:]
- return value
- }
- return take(encryptionLength), take(integrityLength), take(encryptionLength), take(integrityLength), nil
- }
- func espSuiteNames(suite espSuite) (encryption string, integrity string) {
- encryption = fmt.Sprintf("aes-cbc-%d", suite.EncryptionBits)
- switch suite.IntegrityID {
- case integrityHMACSHA1_96:
- integrity = "hmac-sha1-96"
- case integrityHMACSHA256_128:
- integrity = "hmac-sha2-256-128"
- }
- return encryption, integrity
- }
|