digest_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package ims
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "reflect"
  6. "strings"
  7. "testing"
  8. "vocat/internal/vowifi"
  9. )
  10. type recordingAKA struct {
  11. result vowifi.AKAResult
  12. err error
  13. challenges []vowifi.AKAChallenge
  14. }
  15. func (aka *recordingAKA) CheckReady(context.Context, vowifi.SIMIdentity) (vowifi.AKAEvidence, error) {
  16. return vowifi.AKAEvidence{Ready: true, Application: "usim"}, nil
  17. }
  18. func (aka *recordingAKA) Authenticate(
  19. _ context.Context,
  20. _ vowifi.SIMIdentity,
  21. challenge vowifi.AKAChallenge,
  22. ) (vowifi.AKAResult, error) {
  23. aka.challenges = append(aka.challenges, challenge)
  24. return aka.result, aka.err
  25. }
  26. func TestDigestResponseRFC2617Vector(t *testing.T) {
  27. got := digestResponse(
  28. "Mufasa",
  29. "[email protected]",
  30. []byte("Circle Of Life"),
  31. "GET",
  32. "/dir/index.html",
  33. "dcd98b7102dd2f0e8b11d0f600bfb0c093",
  34. "00000001",
  35. "0a4f113b",
  36. "auth",
  37. )
  38. const want = "6629fae49393a05397450978507c4ef1"
  39. if got != want {
  40. t.Fatalf("digestResponse() = %q, want %q", got, want)
  41. }
  42. }
  43. func TestAuthenticateAKAMapsNonceToTypedChallenge(t *testing.T) {
  44. nonceBytes := make([]byte, 40)
  45. for index := range nonceBytes {
  46. nonceBytes[index] = byte(index)
  47. }
  48. aka := &recordingAKA{
  49. result: vowifi.AKAResult{RES: []byte{0xde, 0xad, 0xbe, 0xef}},
  50. }
  51. material, err := authenticateAKA(
  52. context.Background(),
  53. aka,
  54. vowifi.SIMIdentity{IMSI: "001010123456789"},
  55. digestChallenge{Nonce: base64.StdEncoding.EncodeToString(nonceBytes)},
  56. )
  57. if err != nil {
  58. t.Fatalf("authenticateAKA() error = %v", err)
  59. }
  60. if !reflect.DeepEqual(material.password, []byte{0xde, 0xad, 0xbe, 0xef}) {
  61. t.Fatalf("password = %x, want deadbeef", material.password)
  62. }
  63. if len(aka.challenges) != 1 {
  64. t.Fatalf("challenge count = %d, want 1", len(aka.challenges))
  65. }
  66. var wantRAND, wantAUTN [16]byte
  67. copy(wantRAND[:], nonceBytes[:16])
  68. copy(wantAUTN[:], nonceBytes[16:32])
  69. if !reflect.DeepEqual(aka.challenges[0].RAND, wantRAND) ||
  70. !reflect.DeepEqual(aka.challenges[0].AUTN, wantAUTN) {
  71. t.Fatalf("typed challenge = %#v", aka.challenges[0])
  72. }
  73. }
  74. func TestAuthenticateAKAReturnsSynchronizationEvidence(t *testing.T) {
  75. nonce := base64.StdEncoding.EncodeToString(make([]byte, 32))
  76. auts := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
  77. aka := &recordingAKA{
  78. result: vowifi.AKAResult{
  79. AUTS: auts,
  80. SynchronizationFailure: true,
  81. },
  82. }
  83. material, err := authenticateAKA(
  84. context.Background(),
  85. aka,
  86. vowifi.SIMIdentity{},
  87. digestChallenge{Nonce: nonce},
  88. )
  89. if err != nil {
  90. t.Fatalf("authenticateAKA() error = %v", err)
  91. }
  92. if !reflect.DeepEqual(material.auts, auts) || len(material.password) != 0 {
  93. t.Fatalf("material = %#v", material)
  94. }
  95. }
  96. func TestBuildDigestAuthorizationCarriesAUTSWithEmptyPassword(t *testing.T) {
  97. authorization := buildDigestAuthorization(
  98. digestChallenge{
  99. Realm: "ims.example",
  100. Nonce: "nonce",
  101. Algorithm: "AKAv1-MD5",
  102. QOP: "auth",
  103. },
  104. digestCredentials{
  105. Username: "[email protected]",
  106. Password: nil,
  107. AUTS: "AAECAwQFBgcICQoLDA0=",
  108. URI: "sip:ims.example",
  109. Method: "REGISTER",
  110. CNonce: "cnonce",
  111. NC: 1,
  112. },
  113. )
  114. directives, err := parseAuthDirectives(strings.TrimPrefix(authorization, "Digest "))
  115. if err != nil {
  116. t.Fatalf("parseAuthDirectives() error = %v", err)
  117. }
  118. if directives["auts"] != "AAECAwQFBgcICQoLDA0=" {
  119. t.Fatalf("AUTS = %q", directives["auts"])
  120. }
  121. expected := digestResponse(
  122. "[email protected]",
  123. "ims.example",
  124. nil,
  125. "REGISTER",
  126. "sip:ims.example",
  127. "nonce",
  128. "00000001",
  129. "cnonce",
  130. "auth",
  131. )
  132. if directives["response"] != expected {
  133. t.Fatalf("response = %q, want %q", directives["response"], expected)
  134. }
  135. }
  136. func TestParseDigestChallengeSelectsAuth(t *testing.T) {
  137. challenge, err := parseDigestChallenge(
  138. `Digest realm="ims.example", nonce="abc", algorithm=AKAv1-MD5, qop="auth-int, auth", opaque="x\"y"`,
  139. false,
  140. )
  141. if err != nil {
  142. t.Fatalf("parseDigestChallenge() error = %v", err)
  143. }
  144. if challenge.QOP != "auth" || challenge.Opaque != `x"y` {
  145. t.Fatalf("challenge = %#v", challenge)
  146. }
  147. }