| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- package ike
- import (
- "bytes"
- "context"
- "errors"
- "strings"
- "testing"
- "vocat/internal/vowifi"
- )
- type testAKAProvider struct {
- result vowifi.AKAResult
- err error
- challenge vowifi.AKAChallenge
- calls int
- }
- func (provider *testAKAProvider) CheckReady(context.Context, vowifi.SIMIdentity) (vowifi.AKAEvidence, error) {
- return vowifi.AKAEvidence{Ready: true, Application: "USIM"}, nil
- }
- func (provider *testAKAProvider) Authenticate(
- _ context.Context,
- _ vowifi.SIMIdentity,
- challenge vowifi.AKAChallenge,
- ) (vowifi.AKAResult, error) {
- provider.calls++
- provider.challenge = challenge
- return provider.result, provider.err
- }
- func testSIMIdentity() vowifi.SIMIdentity {
- return vowifi.SIMIdentity{
- IMSI: "234150123456789",
- HomeMCC: "234",
- HomeMNC: "15",
- }
- }
- func TestEAPAKAIdentityRequiresExactlyOneRequestAttribute(t *testing.T) {
- client, err := newAKAClient(testSIMIdentity(), &testAKAProvider{})
- if err != nil {
- t.Fatalf("newAKAClient() error = %v", err)
- }
- request, err := marshalEAPPacket(eapPacket{
- Code: eapRequest,
- Identifier: 9,
- Type: eapTypeAKA,
- Data: []byte{akaSubtypeIdentity, 0, 0},
- })
- if err != nil {
- t.Fatal(err)
- }
- action, err := client.handle(context.Background(), request)
- if err != nil {
- t.Fatalf("handle malformed identity error = %v", err)
- }
- response, err := parseEAPPacket(action.Response)
- if err != nil {
- t.Fatal(err)
- }
- if response.Code != eapResponse || response.Identifier != 9 ||
- response.Type != eapTypeAKA || len(response.Data) < 1 ||
- response.Data[0] != akaSubtypeClientError {
- t.Fatalf("malformed identity response = %#v", response)
- }
- permanent, _ := marshalAKAAttribute(akaAttrPermanentIDReq, []byte{0, 0})
- validRequest, _ := marshalEAPPacket(eapPacket{
- Code: eapRequest,
- Identifier: 10,
- Type: eapTypeAKA,
- Data: append([]byte{akaSubtypeIdentity, 0, 0}, permanent...),
- })
- action, err = client.handle(context.Background(), validRequest)
- if err != nil {
- t.Fatalf("handle valid identity error = %v", err)
- }
- response, _ = parseEAPPacket(action.Response)
- if response.Data[0] != akaSubtypeIdentity {
- t.Fatalf("valid identity response subtype = %d", response.Data[0])
- }
- attributes, err := parseAKAAttributes(response.Data[3:])
- if err != nil {
- t.Fatal(err)
- }
- identity, err := oneAKAAttribute(attributes, akaAttrIdentity)
- if err != nil {
- t.Fatal(err)
- }
- length := int(identity.Raw[2])<<8 | int(identity.Raw[3])
- if got := string(identity.Raw[4 : 4+length]); got != "[email protected]" {
- t.Fatalf("permanent AKA identity = %q", got)
- }
- }
- func TestEAPFailureReportsAuthenticationStage(t *testing.T) {
- client, err := newAKAClient(testSIMIdentity(), &testAKAProvider{})
- if err != nil {
- t.Fatal(err)
- }
- failure, err := marshalEAPPacket(eapPacket{Code: eapFailure, Identifier: 3})
- if err != nil {
- t.Fatal(err)
- }
- _, err = client.handle(context.Background(), failure)
- if !errors.Is(err, vowifi.ErrEAPAuthenticationRejected) || !strings.Contains(err.Error(), "before the SIM AKA challenge") {
- t.Fatalf("pre-challenge failure = %v", err)
- }
- client.challengeComplete = true
- _, err = client.handle(context.Background(), failure)
- if !errors.Is(err, vowifi.ErrEAPAuthenticationRejected) || !strings.Contains(err.Error(), "after the SIM AKA response") {
- t.Fatalf("post-challenge failure = %v", err)
- }
- }
- func TestEAPAKAChallengeTypedSIMAndMAC(t *testing.T) {
- result := vowifi.AKAResult{
- RES: bytes.Repeat([]byte{0x91}, 8),
- CK: bytes.Repeat([]byte{0x92}, 16),
- IK: bytes.Repeat([]byte{0x93}, 16),
- }
- provider := &testAKAProvider{result: result}
- client, err := newAKAClient(testSIMIdentity(), provider)
- if err != nil {
- t.Fatal(err)
- }
- keys, err := deriveAKAKeys(client.identity, result.IK, result.CK)
- if err != nil {
- t.Fatal(err)
- }
- randValue := bytes.Repeat([]byte{0xa1}, 16)
- autnValue := bytes.Repeat([]byte{0xa2}, 16)
- randAttribute, _ := marshalAKAAttribute(akaAttrRAND, append([]byte{0, 0}, randValue...))
- autnAttribute, _ := marshalAKAAttribute(akaAttrAUTN, append([]byte{0, 0}, autnValue...))
- macAttribute, _ := marshalAKAAttribute(akaAttrMAC, make([]byte, 18))
- data := []byte{akaSubtypeChallenge, 0, 0}
- data = append(data, randAttribute...)
- data = append(data, autnAttribute...)
- data = append(data, macAttribute...)
- request, _ := marshalEAPPacket(eapPacket{
- Code: eapRequest,
- Identifier: 21,
- Type: eapTypeAKA,
- Data: data,
- })
- attributes, _ := parseAKAAttributes(data[3:])
- mac, _ := oneAKAAttribute(attributes, akaAttrMAC)
- macOffset := 5 + 3 + mac.Offset
- copy(request[macOffset+4:macOffset+20], akaMAC(keys.KAut, request))
- action, err := client.handle(context.Background(), request)
- if err != nil {
- t.Fatalf("handle challenge error = %v", err)
- }
- if provider.calls != 1 || !bytes.Equal(provider.challenge.RAND[:], randValue) ||
- !bytes.Equal(provider.challenge.AUTN[:], autnValue) {
- t.Fatalf("typed SIM challenge = %#v calls=%d", provider.challenge, provider.calls)
- }
- response, err := parseEAPPacket(action.Response)
- if err != nil {
- t.Fatal(err)
- }
- if response.Data[0] != akaSubtypeChallenge {
- t.Fatalf("challenge response subtype = %d", response.Data[0])
- }
- responseAttributes, err := parseAKAAttributes(response.Data[3:])
- if err != nil {
- t.Fatal(err)
- }
- res, err := oneAKAAttribute(responseAttributes, akaAttrRES)
- if err != nil {
- t.Fatal(err)
- }
- if bits := int(res.Raw[2])<<8 | int(res.Raw[3]); bits != len(result.RES)*8 {
- t.Fatalf("AT_RES bits = %d", bits)
- }
- responseMAC, err := oneAKAAttribute(responseAttributes, akaAttrMAC)
- if err != nil {
- t.Fatal(err)
- }
- zeroed := append([]byte(nil), action.Response...)
- responseOffset := 5 + 3 + responseMAC.Offset
- actualMAC := append([]byte(nil), zeroed[responseOffset+4:responseOffset+20]...)
- for index := responseOffset + 4; index < responseOffset+20; index++ {
- zeroed[index] = 0
- }
- if !bytes.Equal(actualMAC, akaMAC(keys.KAut, zeroed)) {
- t.Fatal("response AT_MAC does not authenticate the complete EAP packet")
- }
- success, _ := marshalEAPPacket(eapPacket{Code: eapSuccess, Identifier: 22})
- finalAction, err := client.handle(context.Background(), success)
- if err != nil || !finalAction.Success {
- t.Fatalf("authenticated EAP success = %#v err=%v", finalAction, err)
- }
- }
- func TestEAPAKAUSIMNetworkAuthenticationFailureSendsReject(t *testing.T) {
- provider := &testAKAProvider{err: vowifi.ErrEC20AKAMACFailure}
- client, err := newAKAClient(testSIMIdentity(), provider)
- if err != nil {
- t.Fatal(err)
- }
- randAttribute, _ := marshalAKAAttribute(akaAttrRAND, append([]byte{0, 0}, bytes.Repeat([]byte{1}, 16)...))
- autnAttribute, _ := marshalAKAAttribute(akaAttrAUTN, append([]byte{0, 0}, bytes.Repeat([]byte{2}, 16)...))
- macAttribute, _ := marshalAKAAttribute(akaAttrMAC, make([]byte, 18))
- data := append([]byte{akaSubtypeChallenge, 0, 0}, randAttribute...)
- data = append(data, autnAttribute...)
- data = append(data, macAttribute...)
- request, _ := marshalEAPPacket(eapPacket{Code: eapRequest, Identifier: 5, Type: eapTypeAKA, Data: data})
- action, err := client.handle(context.Background(), request)
- if err != nil {
- t.Fatalf("handle USIM MAC failure error = %v", err)
- }
- response, _ := parseEAPPacket(action.Response)
- if len(response.Data) != 3 || response.Data[0] != akaSubtypeAuthReject {
- t.Fatalf("USIM MAC failure response = %#v", response)
- }
- if !errors.Is(provider.err, vowifi.ErrEC20AKAMACFailure) {
- t.Fatal("test provider lost the MAC failure sentinel")
- }
- }
|