security_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package ims
  2. import (
  3. "errors"
  4. "net"
  5. "reflect"
  6. "strings"
  7. "testing"
  8. )
  9. func TestParseSecurityAgreementSelectsSupportedIPSec(t *testing.T) {
  10. proposal := securityProposal{
  11. spiClient: 1001,
  12. spiServer: 1002,
  13. portClient: 40666,
  14. portServer: 55610,
  15. }
  16. selected := "ipsec-3gpp;q=0.100;alg=hmac-sha-1-96;prot=esp;mod=trans;" +
  17. "ealg=aes-cbc;spi-c=2001;spi-s=2002;port-c=50601;port-s=50600"
  18. unsupported := "digest;q=0.900"
  19. agreement, err := parseSecurityAgreement(
  20. []string{unsupported + ", " + selected},
  21. proposal,
  22. )
  23. if err != nil {
  24. t.Fatalf("parseSecurityAgreement() error = %v", err)
  25. }
  26. if agreement.selected.spiClient != 2001 ||
  27. agreement.selected.spiServer != 2002 ||
  28. agreement.selected.portClient != 50601 ||
  29. agreement.selected.portServer != 50600 {
  30. t.Fatalf("selected mechanism = %#v", agreement.selected)
  31. }
  32. if agreement.verifyValue != unsupported+", "+selected {
  33. t.Fatalf("Security-Verify = %q", agreement.verifyValue)
  34. }
  35. }
  36. func TestParseSecurityAgreementFailsClosed(t *testing.T) {
  37. proposal := securityProposal{
  38. spiClient: 1001,
  39. spiServer: 1002,
  40. portClient: 40666,
  41. portServer: 55610,
  42. }
  43. valid := "ipsec-3gpp;q=0.100;alg=hmac-sha-1-96;prot=esp;mod=trans;" +
  44. "ealg=aes-cbc;spi-c=2001;spi-s=2002;port-c=50601;port-s=50600"
  45. for _, test := range []struct {
  46. name string
  47. values []string
  48. }{
  49. {
  50. name: "unsupported integrity algorithm",
  51. values: []string{
  52. strings.Replace(valid, "hmac-sha-1-96", "hmac-md5-96", 1),
  53. },
  54. },
  55. {
  56. name: "server SPI collides with UE SPI",
  57. values: []string{
  58. strings.Replace(valid, "spi-c=2001", "spi-c=1001", 1),
  59. },
  60. },
  61. {
  62. name: "server SPIs collide",
  63. values: []string{
  64. strings.Replace(valid, "spi-s=2002", "spi-s=2001", 1),
  65. },
  66. },
  67. {
  68. name: "malformed ipsec offer poisons otherwise valid list",
  69. values: []string{
  70. valid + ", ipsec-3gpp;q=0.200;alg=hmac-sha-1-96;alg=hmac-sha-1-96",
  71. },
  72. },
  73. {
  74. name: "no offer",
  75. values: nil,
  76. },
  77. } {
  78. t.Run(test.name, func(t *testing.T) {
  79. _, err := parseSecurityAgreement(test.values, proposal)
  80. if err == nil {
  81. t.Fatal("parseSecurityAgreement() error = nil")
  82. }
  83. })
  84. }
  85. }
  86. func TestExpandIPSecKeys(t *testing.T) {
  87. ck := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
  88. ik := []byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
  89. encryption, integrity, err := expandIPSecKeys(ck, ik)
  90. if err != nil {
  91. t.Fatalf("expandIPSecKeys() error = %v", err)
  92. }
  93. if !reflect.DeepEqual(encryption, ck) {
  94. t.Fatalf("encryption key = %v, want %v", encryption, ck)
  95. }
  96. wantIntegrity := append(append([]byte(nil), ik...), 0, 0, 0, 0)
  97. if !reflect.DeepEqual(integrity, wantIntegrity) {
  98. t.Fatalf("integrity key = %v, want %v", integrity, wantIntegrity)
  99. }
  100. encryption[0] ^= 0xff
  101. integrity[0] ^= 0xff
  102. if ck[0] != 0 || ik[0] != 16 {
  103. t.Fatal("expanded keys alias AKA key material")
  104. }
  105. }
  106. func TestXFRMPlanContainsFourStatesAndProtocolSpecificPolicies(t *testing.T) {
  107. config := testIPSecSAConfig()
  108. install, err := buildXFRMInstallPlan(config)
  109. if err != nil {
  110. t.Fatalf("buildXFRMInstallPlan() error = %v", err)
  111. }
  112. if len(install) != 10 {
  113. t.Fatalf("install operation count = %d, want 10", len(install))
  114. }
  115. for index, operation := range install[:4] {
  116. if !containsArguments(operation.arguments, "xfrm", "state", "add") {
  117. t.Fatalf("operation %d is not a state add: %v", index, operation.arguments)
  118. }
  119. }
  120. for index, operation := range install[4:] {
  121. if !containsArguments(operation.arguments, "xfrm", "policy", "add") {
  122. t.Fatalf("operation %d is not a policy add: %v", index+4, operation.arguments)
  123. }
  124. }
  125. clientReqID := argumentAfter(t, install[0].arguments, "reqid")
  126. if got := argumentAfter(t, install[1].arguments, "reqid"); got != clientReqID {
  127. t.Fatalf("client SA pair reqids = %q and %q", clientReqID, got)
  128. }
  129. serverReqID := argumentAfter(t, install[2].arguments, "reqid")
  130. if got := argumentAfter(t, install[3].arguments, "reqid"); got != serverReqID {
  131. t.Fatalf("server SA pair reqids = %q and %q", serverReqID, got)
  132. }
  133. if clientReqID == serverReqID {
  134. t.Fatalf("SA pair reqids both equal %q", clientReqID)
  135. }
  136. wantPolicies := map[string]bool{
  137. "tcp 40666 50600 out": false,
  138. "udp 40666 50600 out": false,
  139. "tcp 50600 40666 in": false,
  140. "tcp 50601 55610 in": false,
  141. "udp 50601 55610 in": false,
  142. "tcp 55610 50601 out": false,
  143. }
  144. for _, operation := range install[4:] {
  145. key := strings.Join([]string{
  146. argumentAfter(t, operation.arguments, "proto"),
  147. argumentAfter(t, operation.arguments, "sport"),
  148. argumentAfter(t, operation.arguments, "dport"),
  149. argumentAfter(t, operation.arguments, "dir"),
  150. }, " ")
  151. if _, expected := wantPolicies[key]; !expected {
  152. t.Fatalf("unexpected policy %q: %v", key, operation.arguments)
  153. }
  154. wantPolicies[key] = true
  155. }
  156. for policy, found := range wantPolicies {
  157. if !found {
  158. t.Errorf("missing policy %q", policy)
  159. }
  160. }
  161. cleanup := buildXFRMCleanupPlan(config)
  162. if len(cleanup) != 10 {
  163. t.Fatalf("cleanup operation count = %d, want 10", len(cleanup))
  164. }
  165. keyHex := "0x" + strings.Repeat("11", 16)
  166. for _, operation := range cleanup {
  167. if strings.Contains(strings.Join(operation.arguments, " "), keyHex) {
  168. t.Fatalf("cleanup operation retained encryption key: %v", operation.arguments)
  169. }
  170. }
  171. }
  172. func TestValidateIPSecSAConfigRejectsDuplicateSPI(t *testing.T) {
  173. config := testIPSecSAConfig()
  174. config.PCSCFServerSPI = config.UEClientSPI
  175. if err := validateIPSecSAConfig(config); err == nil {
  176. t.Fatal("validateIPSecSAConfig() error = nil")
  177. }
  178. }
  179. func testIPSecSAConfig() IPSecSAConfig {
  180. return IPSecSAConfig{
  181. LocalIP: net.ParseIP("10.0.0.2"),
  182. RemoteIP: net.ParseIP("10.0.0.3"),
  183. UEClientSPI: 0x10000001,
  184. UEServerSPI: 0x10000002,
  185. PCSCFClientSPI: 0x20000001,
  186. PCSCFServerSPI: 0x20000002,
  187. UEClientPort: 40666,
  188. UEServerPort: 55610,
  189. PCSCFClientPort: 50601,
  190. PCSCFServerPort: 50600,
  191. EncryptionKey: []byte(strings.Repeat("\x11", 16)),
  192. IntegrityKey: []byte(strings.Repeat("\x22", 20)),
  193. }
  194. }
  195. func argumentAfter(t *testing.T, arguments []string, name string) string {
  196. t.Helper()
  197. for index := 0; index+1 < len(arguments); index++ {
  198. if arguments[index] == name {
  199. return arguments[index+1]
  200. }
  201. }
  202. t.Fatalf("arguments %v omit %q", arguments, name)
  203. return ""
  204. }
  205. func containsArguments(arguments []string, sequence ...string) bool {
  206. if len(sequence) == 0 || len(sequence) > len(arguments) {
  207. return false
  208. }
  209. for start := 0; start+len(sequence) <= len(arguments); start++ {
  210. if reflect.DeepEqual(arguments[start:start+len(sequence)], sequence) {
  211. return true
  212. }
  213. }
  214. return false
  215. }
  216. func TestErrorsExposeAgreementSentinel(t *testing.T) {
  217. _, err := parseSecurityAgreement(nil, securityProposal{})
  218. if !errors.Is(err, ErrIPSecAgreementRequired) {
  219. t.Fatalf("error = %v, want ErrIPSecAgreementRequired", err)
  220. }
  221. }