| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- package ike
- import (
- "bytes"
- "crypto/cipher"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "math"
- "net"
- "testing"
- )
- type espRepeatingReader byte
- func (value espRepeatingReader) Read(destination []byte) (int, error) {
- for index := range destination {
- destination[index] = byte(value)
- }
- return len(destination), nil
- }
- func TestESPTunnelRoundTripNegotiatedSuites(t *testing.T) {
- t.Parallel()
- tests := []struct {
- name string
- encryption string
- integrity string
- encKey []byte
- authKey []byte
- }{
- {
- name: "AES128-SHA1",
- encryption: "aes-cbc-128",
- integrity: "hmac-sha1-96",
- encKey: bytes.Repeat([]byte{0x11}, 16),
- authKey: bytes.Repeat([]byte{0x22}, 20),
- },
- {
- name: "AES256-SHA256",
- encryption: "aes-cbc-256",
- integrity: "hmac-sha2-256-128",
- encKey: bytes.Repeat([]byte{0x33}, 32),
- authKey: bytes.Repeat([]byte{0x44}, 32),
- },
- }
- for _, test := range tests {
- test := test
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
- tunnel := mustTestESPTunnel(t, test.encryption, test.integrity, test.encKey, test.authKey)
- outbound := testIPv4UDPPacket(
- net.IPv4(10, 0, 0, 2),
- net.IPv4(10, 0, 0, 9),
- 40123,
- 50600,
- []byte("REGISTER"),
- )
- protected, err := tunnel.seal(outbound)
- if err != nil {
- t.Fatalf("seal: %v", err)
- }
- if got := binary.BigEndian.Uint32(protected[0:4]); got != 0x11223344 {
- t.Fatalf("SPI = %#x, want %#x", got, uint32(0x11223344))
- }
- if got := binary.BigEndian.Uint32(protected[4:8]); got != 1 {
- t.Fatalf("sequence = %d, want 1", got)
- }
- // A peer opens the outbound SA with the same SPI and key material.
- peer := mustTestESPTunnel(t, test.encryption, test.integrity, test.encKey, test.authKey)
- peer.initiatorSelectors, peer.responderSelectors =
- peer.responderSelectors, peer.initiatorSelectors
- cleartext, err := peer.open(protected)
- if err != nil {
- t.Fatalf("open: %v", err)
- }
- if !bytes.Equal(cleartext, outbound) {
- t.Fatalf("round trip changed the inner packet")
- }
- })
- }
- }
- func TestESPEncryptionMatchesRFC3602TunnelModeVector(t *testing.T) {
- t.Parallel()
- // RFC 3602 section 4, case #7. Authentication is intentionally outside
- // that RFC vector, so this assertion covers ESP SPI/sequence/IV layout,
- // tunnel-mode padding/trailer, and all 96 AES-CBC ciphertext octets.
- key := mustDecodeHex(t, "0123456789abcdef0123456789abcdef")
- iv := mustDecodeHex(t, "f4e765244f6407adf13dc1380f673f37")
- innerPacket := mustDecodeHex(t,
- "45000054090400004001f988c0a87b03c0a87bc8"+
- "08009f76a90a0100b49c083d02a20400"+
- "08090a0b0c0d0e0f1011121314151617"+
- "18191a1b1c1d1e1f2021222324252627"+
- "28292a2b2c2d2e2f3031323334353637",
- )
- expectedCiphertext := mustDecodeHex(t,
- "773b5241a4c449225e4f3ce5ed611b0c"+
- "237ca96cf74a93013c1b0ea1a0cf70f8"+
- "e4ecaec78ac53aad7a0f022b859243c6"+
- "47752e94a859352b8a4d4d2decd136e5"+
- "c177f132ad3fbfb2201ac9904c74ee0a"+
- "109e0ca1e4dfe9d5a100b842f1c22f0d",
- )
- direction, err := newESPDirection(
- 0x8765,
- key,
- bytes.Repeat([]byte{0x5a}, 20),
- "aes-cbc-128",
- "hmac-sha1-96",
- bytes.NewReader(iv),
- )
- if err != nil {
- t.Fatal(err)
- }
- direction.sequence = 1
- packet, err := direction.seal(innerPacket, 4)
- if err != nil {
- t.Fatal(err)
- }
- if got := packet[:8]; !bytes.Equal(got, mustDecodeHex(t, "0000876500000002")) {
- t.Fatalf("ESP header = %x", got)
- }
- if got := packet[8:24]; !bytes.Equal(got, iv) {
- t.Fatalf("ESP IV = %x", got)
- }
- if got := packet[24 : 24+len(expectedCiphertext)]; !bytes.Equal(got, expectedCiphertext) {
- t.Fatalf("ESP ciphertext = %x, want %x", got, expectedCiphertext)
- }
- }
- func TestESPRejectsTamperWrongSPIAndReplay(t *testing.T) {
- t.Parallel()
- tunnel := mustTestESPTunnel(
- t,
- "aes-cbc-128",
- "hmac-sha1-96",
- bytes.Repeat([]byte{0x51}, 16),
- bytes.Repeat([]byte{0x61}, 20),
- )
- peer := mustTestESPTunnel(
- t,
- "aes-cbc-128",
- "hmac-sha1-96",
- bytes.Repeat([]byte{0x51}, 16),
- bytes.Repeat([]byte{0x61}, 20),
- )
- peer.initiatorSelectors, peer.responderSelectors =
- peer.responderSelectors, peer.initiatorSelectors
- inner := testIPv4UDPPacket(
- net.IPv4(10, 0, 0, 2),
- net.IPv4(10, 0, 0, 9),
- 42000,
- 50600,
- []byte("payload"),
- )
- protected, err := tunnel.seal(inner)
- if err != nil {
- t.Fatal(err)
- }
- tampered := append([]byte(nil), protected...)
- tampered[len(tampered)-1] ^= 0x80
- if _, err := peer.open(tampered); !errors.Is(err, errESPAuthentication) {
- t.Fatalf("tampered packet error = %v, want authentication failure", err)
- }
- wrongSPI := append([]byte(nil), protected...)
- wrongSPI[0] ^= 0x01
- if _, err := peer.open(wrongSPI); err == nil {
- t.Fatal("packet with wrong SPI was accepted")
- }
- if _, err := peer.open(protected); err != nil {
- t.Fatalf("first authenticated packet: %v", err)
- }
- if _, err := peer.open(protected); !errors.Is(err, errESPReplay) {
- t.Fatalf("replayed packet error = %v, want replay rejection", err)
- }
- }
- func TestESPReplayWindowAcceptsAuthenticatedReordering(t *testing.T) {
- t.Parallel()
- sender := mustDefaultESPTunnel(t)
- receiver := mustDefaultESPTunnel(t)
- receiver.initiatorSelectors, receiver.responderSelectors =
- receiver.responderSelectors, receiver.initiatorSelectors
- var protected [][]byte
- for index := 0; index < 3; index++ {
- packet := testIPv4UDPPacket(
- net.IPv4(10, 0, 0, 2),
- net.IPv4(10, 0, 0, 9),
- uint16(40000+index),
- 50600,
- []byte{byte(index)},
- )
- value, err := sender.seal(packet)
- if err != nil {
- t.Fatal(err)
- }
- protected = append(protected, value)
- }
- for _, index := range []int{2, 0, 1} {
- if _, err := receiver.open(protected[index]); err != nil {
- t.Fatalf("open sequence %d: %v", index+1, err)
- }
- }
- if _, err := receiver.open(protected[0]); !errors.Is(err, errESPReplay) {
- t.Fatalf("duplicate reordered packet error = %v", err)
- }
- }
- func TestESPRejectsAuthenticatedInvalidPaddingWithoutConsumingSequence(t *testing.T) {
- t.Parallel()
- sender := mustDefaultESPTunnel(t)
- receiver := mustDefaultESPTunnel(t)
- receiver.initiatorSelectors, receiver.responderSelectors =
- receiver.responderSelectors, receiver.initiatorSelectors
- inner := testIPv4UDPPacket(
- net.IPv4(10, 0, 0, 2),
- net.IPv4(10, 0, 0, 9),
- 40000,
- 50600,
- []byte("one"),
- )
- protected, err := sender.seal(inner)
- if err != nil {
- t.Fatal(err)
- }
- malformed := append([]byte(nil), protected...)
- rewriteESPPlaintext(t, receiver.inbound, malformed, func(plaintext []byte) {
- paddingLength := int(plaintext[len(plaintext)-2])
- if paddingLength == 0 {
- plaintext[len(plaintext)-2] = 1
- plaintext[len(plaintext)-3] = 0xff
- return
- }
- plaintext[len(plaintext)-2-paddingLength] ^= 0xff
- })
- if _, err := receiver.open(malformed); err == nil {
- t.Fatal("authenticated packet with invalid padding was accepted")
- }
- if _, err := receiver.open(protected); err != nil {
- t.Fatalf("invalid packet consumed the sequence number: %v", err)
- }
- }
- func TestESPTrafficSelectorsAreEnforcedInBothDirections(t *testing.T) {
- t.Parallel()
- tunnel := mustDefaultESPTunnel(t)
- disallowed := testIPv4UDPPacket(
- net.IPv4(10, 0, 0, 2),
- net.IPv4(203, 0, 113, 10),
- 40000,
- 50600,
- nil,
- )
- if _, err := tunnel.seal(disallowed); err == nil {
- t.Fatal("outbound packet outside responder selector was accepted")
- }
- sender := mustDefaultESPTunnel(t)
- receiver := mustDefaultESPTunnel(t)
- receiver.initiatorSelectors, receiver.responderSelectors =
- receiver.responderSelectors, receiver.initiatorSelectors
- allowed := testIPv4UDPPacket(
- net.IPv4(10, 0, 0, 2),
- net.IPv4(10, 0, 0, 9),
- 40000,
- 50600,
- nil,
- )
- protected, err := sender.seal(allowed)
- if err != nil {
- t.Fatal(err)
- }
- rewriteESPPlaintext(t, receiver.inbound, protected, func(plaintext []byte) {
- copy(plaintext[16:20], net.IPv4(203, 0, 113, 10).To4())
- })
- if _, err := receiver.open(protected); err == nil {
- t.Fatal("authenticated inbound packet outside selectors was accepted")
- }
- }
- func TestESPSequenceExhaustionRequiresRekey(t *testing.T) {
- t.Parallel()
- tunnel := mustDefaultESPTunnel(t)
- tunnel.outbound.sequence = math.MaxUint32
- packet := testIPv4UDPPacket(
- net.IPv4(10, 0, 0, 2),
- net.IPv4(10, 0, 0, 9),
- 40000,
- 50600,
- nil,
- )
- if _, err := tunnel.seal(packet); err == nil {
- t.Fatal("ESP sequence wrapped instead of requiring rekey")
- }
- }
- func TestParseInnerIPv6ESP(t *testing.T) {
- t.Parallel()
- packet := make([]byte, 40+8)
- packet[0] = 0x60
- binary.BigEndian.PutUint16(packet[4:6], 8)
- packet[6] = 50
- packet[7] = 64
- copy(packet[8:24], net.ParseIP("2001:db8::1").To16())
- copy(packet[24:40], net.ParseIP("2001:db8::2").To16())
- metadata, err := parseInnerPacket(packet)
- if err != nil {
- t.Fatal(err)
- }
- if metadata.protocol != 50 || metadata.nextHeader != 41 {
- t.Fatalf("metadata = %+v", metadata)
- }
- }
- func mustDefaultESPTunnel(t *testing.T) *espTunnel {
- t.Helper()
- return mustTestESPTunnel(
- t,
- "aes-cbc-128",
- "hmac-sha1-96",
- bytes.Repeat([]byte{0x31}, 16),
- bytes.Repeat([]byte{0x41}, 20),
- )
- }
- func mustTestESPTunnel(
- t *testing.T,
- encryption string,
- integrity string,
- encryptionKey []byte,
- authenticationKey []byte,
- ) *espTunnel {
- t.Helper()
- selector := func(ip net.IP) trafficSelector {
- return trafficSelector{
- StartPort: 0,
- EndPort: 65535,
- StartIP: append(net.IP(nil), ip.To4()...),
- EndIP: append(net.IP(nil), ip.To4()...),
- }
- }
- tunnel, err := newESPTunnel(ChildSAConfig{
- InboundSPI: 0x11223344,
- OutboundSPI: 0x11223344,
- Encryption: encryption,
- Integrity: integrity,
- InboundEncKey: encryptionKey,
- InboundAuthKey: authenticationKey,
- OutboundEncKey: encryptionKey,
- OutboundAuthKey: authenticationKey,
- InitiatorSelectors: []trafficSelector{selector(net.IPv4(10, 0, 0, 2))},
- ResponderSelectors: []trafficSelector{selector(net.IPv4(10, 0, 0, 9))},
- }, espRepeatingReader(0xa5))
- if err != nil {
- t.Fatal(err)
- }
- return tunnel
- }
- func testIPv4UDPPacket(
- source net.IP,
- destination net.IP,
- sourcePort uint16,
- destinationPort uint16,
- payload []byte,
- ) []byte {
- packet := make([]byte, 20+8+len(payload))
- packet[0] = 0x45
- binary.BigEndian.PutUint16(packet[2:4], uint16(len(packet)))
- packet[8] = 64
- packet[9] = 17
- copy(packet[12:16], source.To4())
- copy(packet[16:20], destination.To4())
- binary.BigEndian.PutUint16(packet[20:22], sourcePort)
- binary.BigEndian.PutUint16(packet[22:24], destinationPort)
- binary.BigEndian.PutUint16(packet[24:26], uint16(8+len(payload)))
- copy(packet[28:], payload)
- return packet
- }
- func rewriteESPPlaintext(
- t *testing.T,
- direction *espDirection,
- packet []byte,
- rewrite func([]byte),
- ) {
- t.Helper()
- blockSize := direction.block.BlockSize()
- authenticatedLength := len(packet) - direction.icvLength
- iv := packet[espHeaderLength : espHeaderLength+blockSize]
- ciphertext := packet[espHeaderLength+blockSize : authenticatedLength]
- plaintext := make([]byte, len(ciphertext))
- cipher.NewCBCDecrypter(direction.block, iv).CryptBlocks(plaintext, ciphertext)
- rewrite(plaintext)
- cipher.NewCBCEncrypter(direction.block, iv).CryptBlocks(ciphertext, plaintext)
- copy(packet[authenticatedLength:], direction.authenticationCode(packet[:authenticatedLength]))
- }
- func mustDecodeHex(t *testing.T, value string) []byte {
- t.Helper()
- decoded, err := hex.DecodeString(value)
- if err != nil {
- t.Fatal(err)
- }
- return decoded
- }
|