diff --git a/.gitignore b/.gitignore index e460293..67eb6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ wireguard-go +cfg/cfg_values.go +generator.exe +generator \ No newline at end of file diff --git a/Makefile b/Makefile index 3f6e407..2cf0e80 100644 --- a/Makefile +++ b/Makefile @@ -29,3 +29,27 @@ clean: rm -f wireguard-go .PHONY: all clean test install generate-version-and-build + + +has_args = +# If the first argument is "cfg_gen"... +ifeq (cfg_gen,$(firstword $(MAKECMDGOALS))) + has_args = yes +endif + +ifdef has_args + # use the rest as arguments for "cfg_gen" + RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) + # ...and turn them into do-nothing targets + $(eval $(RUN_ARGS):;@:) +endif + +ifeq ($(OS),Windows_NT) + gen_run=generator.exe +else + gen_run=./generator +endif + +.PHONY: cfg_gen +cfg_gen: + go build util/cfgGenerator/generator.go && $(gen_run) $(RUN_ARGS) \ No newline at end of file diff --git a/README.md b/README.md index 074f7ec..cbd8f25 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ This is an implementation of WireGuard in Go. ## Usage +### Config gen +The configs are generate from a yml file. If you want to change the config run `make cfg_gen config_name` where `config_name` is the name of the yaml file you'd like to generate from. The script will search in the `cfg/settings/` folder + +### Example: +`make cfg_gen default` will generate a config from `cfg/settings/default.yml` the file will be called `cfg/cfg_values.go` + +Every time you make changes to the yml you need to rerun this. + Most Linux kernel WireGuard users are used to adding an interface with `ip link add wg0 type wireguard`. With wireguard-go, instead simply run: ``` diff --git a/cfg/cfg_def.go b/cfg/cfg_def.go new file mode 100644 index 0000000..150de72 --- /dev/null +++ b/cfg/cfg_def.go @@ -0,0 +1,55 @@ +package cfg + +import "log" + +func init() { + if IsAdvancedSecurityOn() { + if JunkPacketMaxSize <= JunkPacketMinSize { + log.Fatalf( + "MaxSize: %d; should be greater than MinSize: %d", + JunkPacketMaxSize, + JunkPacketMinSize, + ) + } + if JunkPacketCount < 0 { + log.Fatalf("JunkPacketCount should be non negative") + } + const MaxSegmentSize = 2048 - 32 + if 148+InitPacketJunkSize >= MaxSegmentSize { + log.Fatalf( + "init packets should be smaller than maxSegmentSize: %d", + MaxSegmentSize, + ) + } + if 92+ResponsePacketJunkSize >= MaxSegmentSize { + log.Fatalf( + "response packets should be smaller than maxSegmentSize: %d", + MaxSegmentSize, + ) + } + if 64+UnderLoadPacketJunkSize >= MaxSegmentSize { + log.Fatalf( + "underload packets should be smaller than maxSegmentSize: %d", + MaxSegmentSize, + ) + } + if 32+TransportPacketJunkSize >= MaxSegmentSize { + log.Fatalf( + "transport packets should be smaller than maxSegmentSize: %d", + MaxSegmentSize, + ) + } + } else { + if InitPacketJunkSize != 0 || + ResponsePacketJunkSize != 0 || + UnderLoadPacketJunkSize != 0 || + TransportPacketJunkSize != 0 { + + log.Fatal("JunkSizes should be zero when advanced security on") + } + } +} + +func IsAdvancedSecurityOn() bool { + return InitPacketMagicHeader != 1 +} diff --git a/cfg/settings/advanced_security_off.yml b/cfg/settings/advanced_security_off.yml new file mode 100644 index 0000000..83c3161 --- /dev/null +++ b/cfg/settings/advanced_security_off.yml @@ -0,0 +1,11 @@ +junk_packet_count: 5 +junk_packet_min_size: 10 +junk_packet_max_size: 30 +init_packet_junk_size: 0 +response_packet_junk_size: 0 +underload_packet_junk_size: 0 +transport_packet_junk_size: 0 +init_packet_magic_header: 1 +response_packet_magic_header : 2 +underload_packet_magic_header : 3 +transport_packet_magic_header : 4 \ No newline at end of file diff --git a/cfg/settings/default.yml b/cfg/settings/default.yml new file mode 100644 index 0000000..2728914 --- /dev/null +++ b/cfg/settings/default.yml @@ -0,0 +1,11 @@ +junk_packet_count: 5 +junk_packet_min_size: 10 +junk_packet_max_size: 30 +init_packet_junk_size: 30 +response_packet_junk_size: 50 +underload_packet_junk_size: 0 +transport_packet_junk_size: 0 +init_packet_magic_header: 1234567 +response_packet_magic_header : 7654321 +underload_packet_magic_header : 12345687 +transport_packet_magic_header : 146810 \ No newline at end of file diff --git a/device/noise-protocol.go b/device/noise-protocol.go index e8f6145..44bc1ac 100644 --- a/device/noise-protocol.go +++ b/device/noise-protocol.go @@ -15,6 +15,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/poly1305" + "golang.zx2c4.com/wireguard/cfg" "golang.zx2c4.com/wireguard/tai64n" ) @@ -53,10 +54,10 @@ const ( ) const ( - MessageInitiationType = 1 - MessageResponseType = 2 - MessageCookieReplyType = 3 - MessageTransportType = 4 + MessageInitiationType = cfg.InitPacketMagicHeader + MessageResponseType = cfg.ResponsePacketMagicHeader + MessageCookieReplyType = cfg.UnderloadPacketMagicHeader + MessageTransportType = cfg.TransportPacketMagicHeader ) const ( @@ -75,6 +76,20 @@ const ( MessageTransportOffsetContent = 16 ) +var packetSizeToMsgType = map[int]uint32{ + MessageInitiationSize + cfg.InitPacketJunkSize: MessageInitiationType, + MessageResponseSize + cfg.ResponsePacketJunkSize: MessageResponseType, + MessageCookieReplySize + cfg.UnderLoadPacketJunkSize: MessageCookieReplyType, + MessageTransportSize + cfg.TransportPacketJunkSize: MessageTransportType, +} + +var msgTypeToJunkSize = map[uint32]int{ + MessageInitiationType: cfg.InitPacketJunkSize, + MessageResponseType: cfg.ResponsePacketJunkSize, + MessageCookieReplyType: cfg.UnderLoadPacketJunkSize, + MessageTransportType: cfg.TransportPacketJunkSize, +} + /* Type is an 8-bit field, followed by 3 nul bytes, * by marshalling the messages in little-endian byteorder * we can treat these as a 32-bit unsigned int (for now) @@ -174,7 +189,9 @@ func init() { mixHash(&InitialHash, &InitialChainKey, []byte(WGIdentifier)) } -func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, error) { +func (device *Device) CreateMessageInitiation( + peer *Peer, +) (*MessageInitiation, error) { device.staticIdentity.RLock() defer device.staticIdentity.RUnlock() @@ -214,7 +231,12 @@ func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, e ss[:], ) aead, _ := chacha20poly1305.New(key[:]) - aead.Seal(msg.Static[:0], ZeroNonce[:], device.staticIdentity.publicKey[:], handshake.hash[:]) + aead.Seal( + msg.Static[:0], + ZeroNonce[:], + device.staticIdentity.publicKey[:], + handshake.hash[:], + ) handshake.mixHash(msg.Static[:]) // encrypt timestamp @@ -312,14 +334,23 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer { // protect against replay & flood replay := !timestamp.After(handshake.lastTimestamp) - flood := time.Since(handshake.lastInitiationConsumption) <= HandshakeInitationRate + flood := time.Since( + handshake.lastInitiationConsumption, + ) <= HandshakeInitationRate handshake.mutex.RUnlock() if replay { - device.log.Verbosef("%v - ConsumeMessageInitiation: handshake replay @ %v", peer, timestamp) + device.log.Verbosef( + "%v - ConsumeMessageInitiation: handshake replay @ %v", + peer, + timestamp, + ) return nil } if flood { - device.log.Verbosef("%v - ConsumeMessageInitiation: handshake flood", peer) + device.log.Verbosef( + "%v - ConsumeMessageInitiation: handshake flood", + peer, + ) return nil } @@ -348,7 +379,9 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer { return peer } -func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error) { +func (device *Device) CreateMessageResponse( + peer *Peer, +) (*MessageResponse, error) { handshake := &peer.handshake handshake.mutex.Lock() defer handshake.mutex.Unlock() @@ -361,7 +394,10 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error var err error device.indexTable.Delete(handshake.localIndex) - handshake.localIndex, err = device.indexTable.NewIndexForHandshake(peer, handshake) + handshake.localIndex, err = device.indexTable.NewIndexForHandshake( + peer, + handshake, + ) if err != nil { return nil, err } @@ -551,7 +587,9 @@ func (peer *Peer) BeginSymmetricSession() error { // zero handshake setZero(handshake.chainKey[:]) - setZero(handshake.hash[:]) // Doesn't necessarily need to be zeroed. Could be used for something interesting down the line. + setZero( + handshake.hash[:], + ) // Doesn't necessarily need to be zeroed. Could be used for something interesting down the line. setZero(handshake.localEphemeral[:]) peer.handshake.state = handshakeZeroed diff --git a/device/receive.go b/device/receive.go index e24d29f..9112ed0 100644 --- a/device/receive.go +++ b/device/receive.go @@ -16,6 +16,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" + "golang.zx2c4.com/wireguard/cfg" "golang.zx2c4.com/wireguard/conn" ) @@ -55,7 +56,10 @@ func (peer *Peer) keepKeyFreshReceiving() { return } keypair := peer.keypairs.Current() - if keypair != nil && keypair.isInitiator && time.Since(keypair.created) > (RejectAfterTime-KeepaliveTimeout-RekeyTimeout) { + if keypair != nil && keypair.isInitiator && + time.Since( + keypair.created, + ) > (RejectAfterTime-KeepaliveTimeout-RekeyTimeout) { peer.timers.sentLastMinuteHandshake.Store(true) peer.SendHandshakeInitiation(false) } @@ -66,7 +70,10 @@ func (peer *Peer) keepKeyFreshReceiving() { * Every time the bind is updated a new routine is started for * IPv4 and IPv6 (separately) */ -func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.ReceiveFunc) { +func (device *Device) RoutineReceiveIncoming( + maxBatchSize int, + recv conn.ReceiveFunc, +) { recvName := recv.PrettyName() defer func() { device.log.Verbosef("Routine: receive incoming %s - stopped", recvName) @@ -109,7 +116,11 @@ func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.Receive if errors.Is(err, net.ErrClosed) { return } - device.log.Verbosef("Failed to receive %s packet: %v", recvName, err) + device.log.Verbosef( + "Failed to receive %s packet: %v", + recvName, + err, + ) if neterr, ok := err.(net.Error); ok && !neterr.Temporary() { return } @@ -131,8 +142,17 @@ func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.Receive // check size of packet packet := bufsArrs[i][:size] + if cfg.IsAdvancedSecurityOn() { + var junkSize int + if mapMsgType, ok := packetSizeToMsgType[size]; ok { + junkSize = msgTypeToJunkSize[mapMsgType] + } else { + junkSize = cfg.TransportPacketJunkSize + } + // shift junk + packet = packet[junkSize:] + } msgType := binary.LittleEndian.Uint32(packet[:4]) - switch msgType { // check if transport @@ -302,9 +322,14 @@ func (device *Device) RoutineHandshake(id int) { // consume reply if peer := entry.peer; peer.isRunning.Load() { - device.log.Verbosef("Receiving cookie response from %s", elem.endpoint.DstToString()) + device.log.Verbosef( + "Receiving cookie response from %s", + elem.endpoint.DstToString(), + ) if !peer.cookieGenerator.ConsumeReply(&reply) { - device.log.Verbosef("Could not decrypt invalid cookie response") + device.log.Verbosef( + "Could not decrypt invalid cookie response", + ) } } @@ -325,7 +350,10 @@ func (device *Device) RoutineHandshake(id int) { // verify MAC2 field - if !device.cookieChecker.CheckMAC2(elem.packet, elem.endpoint.DstToBytes()) { + if !device.cookieChecker.CheckMAC2( + elem.packet, + elem.endpoint.DstToBytes(), + ) { device.SendHandshakeCookie(&elem) goto skip } @@ -346,9 +374,7 @@ func (device *Device) RoutineHandshake(id int) { switch elem.msgType { case MessageInitiationType: - // unmarshal - var msg MessageInitiation reader := bytes.NewReader(elem.packet) err := binary.Read(reader, binary.LittleEndian, &msg) @@ -358,10 +384,12 @@ func (device *Device) RoutineHandshake(id int) { } // consume initiation - peer := device.ConsumeMessageInitiation(&msg) if peer == nil { - device.log.Verbosef("Received invalid initiation message from %s", elem.endpoint.DstToString()) + device.log.Verbosef( + "Received invalid initiation message from %s", + elem.endpoint.DstToString(), + ) goto skip } @@ -394,7 +422,10 @@ func (device *Device) RoutineHandshake(id int) { peer := device.ConsumeMessageResponse(&msg) if peer == nil { - device.log.Verbosef("Received invalid response message from %s", elem.endpoint.DstToString()) + device.log.Verbosef( + "Received invalid response message from %s", + elem.endpoint.DstToString(), + ) goto skip } @@ -414,7 +445,11 @@ func (device *Device) RoutineHandshake(id int) { err = peer.BeginSymmetricSession() if err != nil { - device.log.Errorf("%v - Failed to derive keypair: %v", peer, err) + device.log.Errorf( + "%v - Failed to derive keypair: %v", + peer, + err, + ) goto skip } @@ -448,7 +483,10 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) { continue } - if !elem.keypair.replayFilter.ValidateCounter(elem.counter, RejectAfterMessages) { + if !elem.keypair.replayFilter.ValidateCounter( + elem.counter, + RejectAfterMessages, + ) { continue } @@ -475,13 +513,17 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) { } field := elem.packet[IPv4offsetTotalLength : IPv4offsetTotalLength+2] length := binary.BigEndian.Uint16(field) - if int(length) > len(elem.packet) || int(length) < ipv4.HeaderLen { + if int(length) > len(elem.packet) || + int(length) < ipv4.HeaderLen { continue } elem.packet = elem.packet[:length] src := elem.packet[IPv4offsetSrc : IPv4offsetSrc+net.IPv4len] if device.allowedips.Lookup(src) != peer { - device.log.Verbosef("IPv4 packet with disallowed source address from %v", peer) + device.log.Verbosef( + "IPv4 packet with disallowed source address from %v", + peer, + ) continue } @@ -498,21 +540,36 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) { elem.packet = elem.packet[:length] src := elem.packet[IPv6offsetSrc : IPv6offsetSrc+net.IPv6len] if device.allowedips.Lookup(src) != peer { - device.log.Verbosef("IPv6 packet with disallowed source address from %v", peer) + device.log.Verbosef( + "IPv6 packet with disallowed source address from %v", + peer, + ) continue } default: - device.log.Verbosef("Packet with invalid IP version from %v", peer) + device.log.Verbosef( + "Packet with invalid IP version from %v", + peer, + ) continue } - bufs = append(bufs, elem.buffer[:MessageTransportOffsetContent+len(elem.packet)]) + bufs = append( + bufs, + elem.buffer[:MessageTransportOffsetContent+len(elem.packet)], + ) } if len(bufs) > 0 { - _, err := device.tun.device.Write(bufs, MessageTransportOffsetContent) + _, err := device.tun.device.Write( + bufs, + MessageTransportOffsetContent, + ) if err != nil && !device.isClosed() { - device.log.Errorf("Failed to write packets to TUN device: %v", err) + device.log.Errorf( + "Failed to write packets to TUN device: %v", + err, + ) } } for _, elem := range *elems { diff --git a/device/send.go b/device/send.go index d22bf26..9cbb7ab 100644 --- a/device/send.go +++ b/device/send.go @@ -9,6 +9,8 @@ import ( "bytes" "encoding/binary" "errors" + "fmt" + "math/rand" "net" "os" "sync" @@ -17,6 +19,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" + "golang.zx2c4.com/wireguard/cfg" "golang.zx2c4.com/wireguard/tun" ) @@ -116,22 +119,47 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error { msg, err := peer.device.CreateMessageInitiation(peer) if err != nil { - peer.device.log.Errorf("%v - Failed to create initiation message: %v", peer, err) + peer.device.log.Errorf( + "%v - Failed to create initiation message: %v", + peer, + err, + ) return err } - + // so only packet processed for cookie generation + var junkedHeader []byte + if cfg.IsAdvancedSecurityOn() { + err = peer.sendJunkPackets() + if err != nil { + peer.device.log.Errorf("%v - %v", peer, err) + return err + } + var buf [cfg.InitPacketJunkSize]byte + writer := bytes.NewBuffer(buf[:0]) + err = appendJunk(writer, cfg.InitPacketJunkSize) + if err != nil { + peer.device.log.Errorf("%v - %v", peer, err) + return err + } + junkedHeader = writer.Bytes() + } var buf [MessageInitiationSize]byte writer := bytes.NewBuffer(buf[:0]) binary.Write(writer, binary.LittleEndian, msg) packet := writer.Bytes() peer.cookieGenerator.AddMacs(packet) + junkedHeader = append(junkedHeader, packet...) peer.timersAnyAuthenticatedPacketTraversal() peer.timersAnyAuthenticatedPacketSent() - err = peer.SendBuffers([][]byte{packet}) + err = peer.SendBuffers([][]byte{junkedHeader}) if err != nil { - peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err) + peer.device.log.Errorf( + "%v - Failed to send handshake initiation: %v", + peer, + err, + ) } peer.timersHandshakeInitiated() @@ -147,15 +175,31 @@ func (peer *Peer) SendHandshakeResponse() error { response, err := peer.device.CreateMessageResponse(peer) if err != nil { - peer.device.log.Errorf("%v - Failed to create response message: %v", peer, err) + peer.device.log.Errorf( + "%v - Failed to create response message: %v", + peer, + err, + ) return err } - + var junkedHeader []byte + if cfg.IsAdvancedSecurityOn() { + var buf [cfg.ResponsePacketJunkSize]byte + writer := bytes.NewBuffer(buf[:0]) + err = appendJunk(writer, cfg.ResponsePacketJunkSize) + if err != nil { + peer.device.log.Errorf("%v - %v", peer, err) + return err + } + junkedHeader = writer.Bytes() + } var buf [MessageResponseSize]byte writer := bytes.NewBuffer(buf[:0]) + binary.Write(writer, binary.LittleEndian, response) packet := writer.Bytes() peer.cookieGenerator.AddMacs(packet) + junkedHeader = append(junkedHeader, packet...) err = peer.BeginSymmetricSession() if err != nil { @@ -168,18 +212,31 @@ func (peer *Peer) SendHandshakeResponse() error { peer.timersAnyAuthenticatedPacketSent() // TODO: allocation could be avoided - err = peer.SendBuffers([][]byte{packet}) + err = peer.SendBuffers([][]byte{junkedHeader}) if err != nil { - peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err) + peer.device.log.Errorf( + "%v - Failed to send handshake response: %v", + peer, + err, + ) } return err } -func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement) error { - device.log.Verbosef("Sending cookie response for denied handshake message for %v", initiatingElem.endpoint.DstToString()) +func (device *Device) SendHandshakeCookie( + initiatingElem *QueueHandshakeElement, +) error { + device.log.Verbosef( + "Sending cookie response for denied handshake message for %v", + initiatingElem.endpoint.DstToString(), + ) sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8]) - reply, err := device.cookieChecker.CreateReply(initiatingElem.packet, sender, initiatingElem.endpoint.DstToBytes()) + reply, err := device.cookieChecker.CreateReply( + initiatingElem.packet, + sender, + initiatingElem.endpoint.DstToBytes(), + ) if err != nil { device.log.Errorf("Failed to create cookie reply: %v", err) return err @@ -199,7 +256,8 @@ func (peer *Peer) keepKeyFreshSending() { return } nonce := keypair.sendNonce.Load() - if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > RekeyAfterTime) { + if nonce > RekeyAfterMessages || + (keypair.isInitiator && time.Since(keypair.created) > RekeyAfterTime) { peer.SendHandshakeInitiation(false) } } @@ -302,12 +360,18 @@ func (device *Device) RoutineReadFromTUN() { // TODO: record stat for this // This will happen if MSS is surprisingly small (< 576) // coincident with reasonably high throughput. - device.log.Verbosef("Dropped some packets from multi-segment read: %v", readErr) + device.log.Verbosef( + "Dropped some packets from multi-segment read: %v", + readErr, + ) continue } if !device.isClosed() { if !errors.Is(readErr, os.ErrClosed) { - device.log.Errorf("Failed to read packet from TUN device: %v", readErr) + device.log.Errorf( + "Failed to read packet from TUN device: %v", + readErr, + ) } go device.Close() } @@ -342,7 +406,8 @@ top: } keypair := peer.keypairs.Current() - if keypair == nil || keypair.sendNonce.Load() >= RejectAfterMessages || time.Since(keypair.created) >= RejectAfterTime { + if keypair == nil || keypair.sendNonce.Load() >= RejectAfterMessages || + time.Since(keypair.created) >= RejectAfterTime { peer.SendHandshakeInitiation(false) return } @@ -373,7 +438,9 @@ top: *elems = (*elems)[:i] if elemsOOO != nil { - peer.StagePackets(elemsOOO) // XXX: Out of order, but we can't front-load go chans + peer.StagePackets( + elemsOOO, + ) // XXX: Out of order, but we can't front-load go chans } if len(*elems) == 0 { @@ -404,6 +471,31 @@ top: } } +func (peer *Peer) sendJunkPackets() error { + junks := make([][]byte, 0, cfg.JunkPacketCount) + for i := 0; i < cfg.JunkPacketCount; i++ { + packetSize := rand.Intn( + cfg.JunkPacketMaxSize-cfg.JunkPacketMinSize, + ) + cfg.JunkPacketMinSize + + junk, err := randomJunkWithSize(packetSize) + if err != nil { + peer.device.log.Errorf( + "%v - Failed to create junk packet: %v", + peer, + err, + ) + return err + } + junks = append(junks, junk) + } + err := peer.SendBuffers(junks) + if err != nil { + return fmt.Errorf("failed to send junks: %v", err) + } + return nil +} + func (peer *Peer) FlushStagedPackets() { for { select { @@ -459,7 +551,10 @@ func (device *Device) RoutineEncryption(id int) { binary.LittleEndian.PutUint64(fieldNonce, elem.nonce) // pad content to multiple of 16 - paddingSize := calculatePaddingSize(len(elem.packet), int(device.tun.mtu.Load())) + paddingSize := calculatePaddingSize( + len(elem.packet), + int(device.tun.mtu.Load()), + ) elem.packet = append(elem.packet, paddingZeros[:paddingSize]...) // encrypt content and release to consumer @@ -478,7 +573,10 @@ func (device *Device) RoutineEncryption(id int) { func (peer *Peer) RoutineSequentialSender(maxBatchSize int) { device := peer.device defer func() { - defer device.log.Verbosef("%v - Routine: sequential sender - stopped", peer) + defer device.log.Verbosef( + "%v - Routine: sequential sender - stopped", + peer, + ) peer.stopping.Done() }() device.log.Verbosef("%v - Routine: sequential sender - started", peer) diff --git a/device/util.go b/device/util.go new file mode 100644 index 0000000..aab8ab7 --- /dev/null +++ b/device/util.go @@ -0,0 +1,25 @@ +package device + +import ( + "bytes" + crand "crypto/rand" + "fmt" +) + +func appendJunk(writer *bytes.Buffer, size int) error { + headerJunk, err := randomJunkWithSize(size) + if err != nil { + return fmt.Errorf("failed to create header junk: %v", err) + } + _, err = writer.Write(headerJunk) + if err != nil { + return fmt.Errorf("failed to write header junk: %v", err) + } + return nil +} + +func randomJunkWithSize(size int) ([]byte, error) { + junk := make([]byte, size) + _, err := crand.Read(junk) + return junk, err +} diff --git a/device/util_test.go b/device/util_test.go new file mode 100644 index 0000000..0a936cf --- /dev/null +++ b/device/util_test.go @@ -0,0 +1,29 @@ +package device + +import ( + "bytes" + "fmt" + "testing" + + "golang.zx2c4.com/wireguard/cfg" +) + +func Test_randomJunktWithSize(t *testing.T) { + junk, err := randomJunkWithSize(30) + fmt.Println(string(junk), len(junk), err) +} + +func Test_appendJunk(t *testing.T) { + t.Run("", func(t *testing.T) { + s := "apple" + buffer := bytes.NewBuffer([]byte(s)) + err := appendJunk(buffer, 30) + if err != nil && + buffer.Len() != len(s)+int(cfg.InitPacketJunkSize) { + t.Errorf("appendWithJunk() size don't match") + } + read := make([]byte, 50) + buffer.Read(read) + fmt.Println(string(read)) + }) +} diff --git a/go.mod b/go.mod index c04e1bb..9ed3e19 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module golang.zx2c4.com/wireguard go 1.20 require ( + github.com/juju/errors v1.0.0 golang.org/x/crypto v0.6.0 golang.org/x/net v0.7.0 golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 + gopkg.in/yaml.v3 v3.0.1 gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 ) diff --git a/go.sum b/go.sum index cfeaee6..aa7e871 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= +github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= @@ -10,5 +14,9 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqG golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY= gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA= diff --git a/util/cfgGenerator/cfg_values.txt b/util/cfgGenerator/cfg_values.txt new file mode 100644 index 0000000..6ca6246 --- /dev/null +++ b/util/cfgGenerator/cfg_values.txt @@ -0,0 +1,15 @@ +// THIS IS A GENERATED FILE; ANY MODIFICATION WILL BE LOST + +package cfg + +const JunkPacketCount int = {{.JunkPacketCount}} +const JunkPacketMinSize int = {{.JunkPacketMinSize}} +const JunkPacketMaxSize int = {{.JunkPacketMaxSize}} +const InitPacketJunkSize int = {{.InitPacketJunkSize}} +const ResponsePacketJunkSize int = {{.ResponsePacketJunkSize}} +const UnderLoadPacketJunkSize int = {{.UnderLoadPacketJunkSize}} +const TransportPacketJunkSize int = {{.TransportPacketJunkSize}} +const InitPacketMagicHeader uint32 = {{.InitPacketMagicHeader}} +const ResponsePacketMagicHeader uint32 = {{.ResponsePacketMagicHeader}} +const UnderloadPacketMagicHeader uint32 = {{.UnderloadPacketMagicHeader}} +const TransportPacketMagicHeader uint32 = {{.TransportPacketMagicHeader}} \ No newline at end of file diff --git a/util/cfgGenerator/generator.go b/util/cfgGenerator/generator.go new file mode 100644 index 0000000..cb14654 --- /dev/null +++ b/util/cfgGenerator/generator.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "html/template" + "os" + "path/filepath" + + "golang.zx2c4.com/wireguard/util/cfgGenerator/internal/config" +) + +func main() { + cfgName := "default" + if len(os.Args) != 1 { + cfgName = os.Args[1] + } else { + fmt.Println("WARNING; config name omited; using default") + } + + ex, err := os.Executable() + if err != nil { + panic(err) + } + mainPath := filepath.Dir(ex) + cfgPath := mainPath + "/cfg/" + cfgFilePath := cfgPath + "settings/" + cfgName + ".yml" + + cfgTmpl, err := template.ParseFiles( + mainPath + "/util/cfgGenerator/cfg_values.txt", + ) + if err != nil { + panic(err) + } + cfg, err := config.NewFromFilename(cfgFilePath) + if err != nil { + panic(err) + } + f, _ := os.Create(cfgPath + "cfg_values.go") + + defer f.Close() + cfgTmpl.Execute(f, *cfg) + f.Sync() +} diff --git a/util/cfgGenerator/internal/config/config.go b/util/cfgGenerator/internal/config/config.go new file mode 100644 index 0000000..0af977c --- /dev/null +++ b/util/cfgGenerator/internal/config/config.go @@ -0,0 +1,58 @@ +package config + +import ( + "io/ioutil" + + "github.com/juju/errors" + "gopkg.in/yaml.v3" +) + +type config struct { + JunkPacketCount int `yaml:"junk_packet_count"` + JunkPacketMinSize int `yaml:"junk_packet_min_size"` + JunkPacketMaxSize int `yaml:"junk_packet_max_size"` + InitPacketJunkSize int `yaml:"init_packet_junk_size"` + ResponsePacketJunkSize int `yaml:"response_packet_junk_size"` + UnderLoadPacketJunkSize int `yaml:"underload_packet_junk_size"` + TransportPacketJunkSize int `yaml:"transport_packet_junk_size"` + InitPacketMagicHeader uint32 `yaml:"init_packet_magic_header"` + ResponsePacketMagicHeader uint32 `yaml:"response_packet_magic_header"` + UnderloadPacketMagicHeader uint32 `yaml:"underload_packet_magic_header"` + TransportPacketMagicHeader uint32 `yaml:"transport_packet_magic_header"` +} + +// New creates a new CW from a file by the given name +func New(name string) (*config, error) { + return NewFromFilename(name) +} + +// NewFromFilename creates a new CW from a file by the given filename +func NewFromFilename(filename string) (*config, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, errors.Trace(err) + } + + return NewFromRaw(data) +} + +// NewFromRaw creates a new CW by unmarshaling the given raw data +func NewFromRaw(raw []byte) (*config, error) { + cfg := &config{} + if err := yaml.Unmarshal(raw, cfg); err != nil { + return nil, errors.Trace(err) + } + + return cfg, nil +} + +// TODO +// String can't be defined on a value receiver here because of the mutex +func (c *config) String() string { + raw, err := yaml.Marshal(c) + if err != nil { + return err.Error() + } + + return string(raw) +}