| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- package ims
- import (
- "errors"
- "net"
- "reflect"
- "strings"
- "testing"
- )
- func TestParseSecurityAgreementSelectsSupportedIPSec(t *testing.T) {
- proposal := securityProposal{
- spiClient: 1001,
- spiServer: 1002,
- portClient: 40666,
- portServer: 55610,
- }
- selected := "ipsec-3gpp;q=0.100;alg=hmac-sha-1-96;prot=esp;mod=trans;" +
- "ealg=aes-cbc;spi-c=2001;spi-s=2002;port-c=50601;port-s=50600"
- unsupported := "digest;q=0.900"
- agreement, err := parseSecurityAgreement(
- []string{unsupported + ", " + selected},
- proposal,
- )
- if err != nil {
- t.Fatalf("parseSecurityAgreement() error = %v", err)
- }
- if agreement.selected.spiClient != 2001 ||
- agreement.selected.spiServer != 2002 ||
- agreement.selected.portClient != 50601 ||
- agreement.selected.portServer != 50600 {
- t.Fatalf("selected mechanism = %#v", agreement.selected)
- }
- if agreement.verifyValue != unsupported+", "+selected {
- t.Fatalf("Security-Verify = %q", agreement.verifyValue)
- }
- }
- func TestParseSecurityAgreementFailsClosed(t *testing.T) {
- proposal := securityProposal{
- spiClient: 1001,
- spiServer: 1002,
- portClient: 40666,
- portServer: 55610,
- }
- valid := "ipsec-3gpp;q=0.100;alg=hmac-sha-1-96;prot=esp;mod=trans;" +
- "ealg=aes-cbc;spi-c=2001;spi-s=2002;port-c=50601;port-s=50600"
- for _, test := range []struct {
- name string
- values []string
- }{
- {
- name: "unsupported integrity algorithm",
- values: []string{
- strings.Replace(valid, "hmac-sha-1-96", "hmac-md5-96", 1),
- },
- },
- {
- name: "server SPI collides with UE SPI",
- values: []string{
- strings.Replace(valid, "spi-c=2001", "spi-c=1001", 1),
- },
- },
- {
- name: "server SPIs collide",
- values: []string{
- strings.Replace(valid, "spi-s=2002", "spi-s=2001", 1),
- },
- },
- {
- name: "malformed ipsec offer poisons otherwise valid list",
- values: []string{
- valid + ", ipsec-3gpp;q=0.200;alg=hmac-sha-1-96;alg=hmac-sha-1-96",
- },
- },
- {
- name: "no offer",
- values: nil,
- },
- } {
- t.Run(test.name, func(t *testing.T) {
- _, err := parseSecurityAgreement(test.values, proposal)
- if err == nil {
- t.Fatal("parseSecurityAgreement() error = nil")
- }
- })
- }
- }
- func TestExpandIPSecKeys(t *testing.T) {
- ck := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
- ik := []byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
- encryption, integrity, err := expandIPSecKeys(ck, ik)
- if err != nil {
- t.Fatalf("expandIPSecKeys() error = %v", err)
- }
- if !reflect.DeepEqual(encryption, ck) {
- t.Fatalf("encryption key = %v, want %v", encryption, ck)
- }
- wantIntegrity := append(append([]byte(nil), ik...), 0, 0, 0, 0)
- if !reflect.DeepEqual(integrity, wantIntegrity) {
- t.Fatalf("integrity key = %v, want %v", integrity, wantIntegrity)
- }
- encryption[0] ^= 0xff
- integrity[0] ^= 0xff
- if ck[0] != 0 || ik[0] != 16 {
- t.Fatal("expanded keys alias AKA key material")
- }
- }
- func TestXFRMPlanContainsFourStatesAndProtocolSpecificPolicies(t *testing.T) {
- config := testIPSecSAConfig()
- install, err := buildXFRMInstallPlan(config)
- if err != nil {
- t.Fatalf("buildXFRMInstallPlan() error = %v", err)
- }
- if len(install) != 10 {
- t.Fatalf("install operation count = %d, want 10", len(install))
- }
- for index, operation := range install[:4] {
- if !containsArguments(operation.arguments, "xfrm", "state", "add") {
- t.Fatalf("operation %d is not a state add: %v", index, operation.arguments)
- }
- }
- for index, operation := range install[4:] {
- if !containsArguments(operation.arguments, "xfrm", "policy", "add") {
- t.Fatalf("operation %d is not a policy add: %v", index+4, operation.arguments)
- }
- }
- clientReqID := argumentAfter(t, install[0].arguments, "reqid")
- if got := argumentAfter(t, install[1].arguments, "reqid"); got != clientReqID {
- t.Fatalf("client SA pair reqids = %q and %q", clientReqID, got)
- }
- serverReqID := argumentAfter(t, install[2].arguments, "reqid")
- if got := argumentAfter(t, install[3].arguments, "reqid"); got != serverReqID {
- t.Fatalf("server SA pair reqids = %q and %q", serverReqID, got)
- }
- if clientReqID == serverReqID {
- t.Fatalf("SA pair reqids both equal %q", clientReqID)
- }
- wantPolicies := map[string]bool{
- "tcp 40666 50600 out": false,
- "udp 40666 50600 out": false,
- "tcp 50600 40666 in": false,
- "tcp 50601 55610 in": false,
- "udp 50601 55610 in": false,
- "tcp 55610 50601 out": false,
- }
- for _, operation := range install[4:] {
- key := strings.Join([]string{
- argumentAfter(t, operation.arguments, "proto"),
- argumentAfter(t, operation.arguments, "sport"),
- argumentAfter(t, operation.arguments, "dport"),
- argumentAfter(t, operation.arguments, "dir"),
- }, " ")
- if _, expected := wantPolicies[key]; !expected {
- t.Fatalf("unexpected policy %q: %v", key, operation.arguments)
- }
- wantPolicies[key] = true
- }
- for policy, found := range wantPolicies {
- if !found {
- t.Errorf("missing policy %q", policy)
- }
- }
- cleanup := buildXFRMCleanupPlan(config)
- if len(cleanup) != 10 {
- t.Fatalf("cleanup operation count = %d, want 10", len(cleanup))
- }
- keyHex := "0x" + strings.Repeat("11", 16)
- for _, operation := range cleanup {
- if strings.Contains(strings.Join(operation.arguments, " "), keyHex) {
- t.Fatalf("cleanup operation retained encryption key: %v", operation.arguments)
- }
- }
- }
- func TestValidateIPSecSAConfigRejectsDuplicateSPI(t *testing.T) {
- config := testIPSecSAConfig()
- config.PCSCFServerSPI = config.UEClientSPI
- if err := validateIPSecSAConfig(config); err == nil {
- t.Fatal("validateIPSecSAConfig() error = nil")
- }
- }
- func testIPSecSAConfig() IPSecSAConfig {
- return IPSecSAConfig{
- LocalIP: net.ParseIP("10.0.0.2"),
- RemoteIP: net.ParseIP("10.0.0.3"),
- UEClientSPI: 0x10000001,
- UEServerSPI: 0x10000002,
- PCSCFClientSPI: 0x20000001,
- PCSCFServerSPI: 0x20000002,
- UEClientPort: 40666,
- UEServerPort: 55610,
- PCSCFClientPort: 50601,
- PCSCFServerPort: 50600,
- EncryptionKey: []byte(strings.Repeat("\x11", 16)),
- IntegrityKey: []byte(strings.Repeat("\x22", 20)),
- }
- }
- func argumentAfter(t *testing.T, arguments []string, name string) string {
- t.Helper()
- for index := 0; index+1 < len(arguments); index++ {
- if arguments[index] == name {
- return arguments[index+1]
- }
- }
- t.Fatalf("arguments %v omit %q", arguments, name)
- return ""
- }
- func containsArguments(arguments []string, sequence ...string) bool {
- if len(sequence) == 0 || len(sequence) > len(arguments) {
- return false
- }
- for start := 0; start+len(sequence) <= len(arguments); start++ {
- if reflect.DeepEqual(arguments[start:start+len(sequence)], sequence) {
- return true
- }
- }
- return false
- }
- func TestErrorsExposeAgreementSentinel(t *testing.T) {
- _, err := parseSecurityAgreement(nil, securityProposal{})
- if !errors.Is(err, ErrIPSecAgreementRequired) {
- t.Fatalf("error = %v, want ErrIPSecAgreementRequired", err)
- }
- }
|