mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-21 15:23:43 +08:00
Fix something
This commit is contained in:
@@ -76,13 +76,13 @@ type carrierProfileDocument struct {
|
||||
}
|
||||
|
||||
type carrierProfileRule struct {
|
||||
ID string `json:"id"`
|
||||
Match carrierProfileMatch `json:"match,omitzero"`
|
||||
MatchAny []carrierProfileMatch `json:"match_any,omitempty"`
|
||||
Route carrierProfileRoute `json:"route,omitzero"`
|
||||
EPDG carrierProfileEPDG `json:"epdg,omitzero"`
|
||||
IKE carrierProfileIKE `json:"ike,omitzero"`
|
||||
IMS carrierProfileIMS `json:"ims,omitzero"`
|
||||
ID string `json:"id"`
|
||||
Match carrierProfileMatch `json:"match,omitzero"`
|
||||
MatchAny []carrierProfileMatch `json:"match_any,omitempty"`
|
||||
Route carrierProfileRoute `json:"route,omitzero"`
|
||||
EPDG carrierProfileEPDG `json:"epdg,omitzero"`
|
||||
IKE carrierProfileIKE `json:"ike,omitzero"`
|
||||
IMS carrierProfileIMS `json:"ims,omitzero"`
|
||||
}
|
||||
|
||||
type carrierProfileMatch struct {
|
||||
@@ -479,8 +479,12 @@ func matchCarrierProfile(match carrierProfileMatch, identity SIMIdentity) (int,
|
||||
}{
|
||||
{name: "imsi", weight: 80, values: match.IMSIPrefixes, actual: identity.IMSI},
|
||||
{name: "iccid", weight: 70, values: match.ICCIDPrefixes, actual: identity.ICCID},
|
||||
{name: "gid1", weight: 50, values: match.GID1Prefixes, actual: identity.GID1, foldCase: true},
|
||||
{name: "gid2", weight: 40, values: match.GID2Prefixes, actual: identity.GID2, foldCase: true},
|
||||
// GID values identify an MVNO/service profile within a host network and
|
||||
// therefore outrank the host issuer's broad ICCID prefix. Otherwise a
|
||||
// home-PLMN+ICCID AT&T rule hides RedPocket/Cricket/etc. even when the SIM
|
||||
// exposes the carrier bundle's exact GID selector.
|
||||
{name: "gid1", weight: 90, values: match.GID1Prefixes, actual: identity.GID1, foldCase: true},
|
||||
{name: "gid2", weight: 85, values: match.GID2Prefixes, actual: identity.GID2, foldCase: true},
|
||||
} {
|
||||
if len(selector.values) == 0 {
|
||||
continue
|
||||
|
||||
@@ -55,6 +55,16 @@ func TestResolveCarrierProfileATT(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCarrierProfileRedPocketOutranksBroadATTICCID(t *testing.T) {
|
||||
profile := ResolveCarrierProfile(SIMIdentity{
|
||||
ICCID: "8901410000000000001", IMSI: "310170000000001",
|
||||
HomeMCC: "310", HomeMNC: "170", SPN: "Red Pocket", GID1: "42FFFF",
|
||||
})
|
||||
if profile.ID != "ipcc-redpocket-310170" || profile.MatchSource != "hplmn+gid1" {
|
||||
t.Fatalf("RedPocket profile = %#v", profile)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCarrierProfileStandardHasNoRegisterOverrides(t *testing.T) {
|
||||
profile := ResolveCarrierProfile(SIMIdentity{HomeMCC: "999", HomeMNC: "99"})
|
||||
if profile.ID != CarrierProfileStandard {
|
||||
|
||||
@@ -570,6 +570,14 @@
|
||||
{
|
||||
"id": "ipcc-redpocket-310170",
|
||||
"match_any": [
|
||||
{
|
||||
"home_plmns": [
|
||||
"310170"
|
||||
],
|
||||
"gid1_prefixes": [
|
||||
"42"
|
||||
]
|
||||
},
|
||||
{
|
||||
"home_plmns": [
|
||||
"310410"
|
||||
@@ -585,6 +593,18 @@
|
||||
"gid1_prefixes": [
|
||||
"42"
|
||||
]
|
||||
},
|
||||
{
|
||||
"home_plmns": [
|
||||
"310170",
|
||||
"310410",
|
||||
"310280"
|
||||
],
|
||||
"spns": [
|
||||
"Red Pocket",
|
||||
"RedPocket",
|
||||
"Red Pocket Mobile"
|
||||
]
|
||||
}
|
||||
],
|
||||
"epdg": {
|
||||
@@ -11783,4 +11803,4 @@
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ var (
|
||||
const (
|
||||
usimAIDPrefix = "A0000000871002"
|
||||
isimAIDPrefix = "A0000000871004"
|
||||
efDIRFileID = 0x2f00
|
||||
efADDecimal = 28589 // 0x6FAD
|
||||
efEHPLMNDecimal = 28441 // 0x6F19 (3GPP TS 31.102 EF_EHPLMN)
|
||||
channelCleanupTimeout = 3 * time.Second
|
||||
@@ -1025,6 +1026,19 @@ func (adapter *EC20Adapter) discoverAKAApplication(
|
||||
}
|
||||
}
|
||||
}
|
||||
// CUAD is optional and is rejected by a number of EC20 firmware branches.
|
||||
// In that case do not immediately fall back to the seven-byte registered
|
||||
// application-provider prefix: cards may expose multiple USIM instances and
|
||||
// require the complete PIX from EF_DIR to select the provisioned one. Read
|
||||
// EF_DIR over the standards-based basic channel, which remains available on
|
||||
// the same firmware that rejects CCHO/CGLA.
|
||||
if discovered, discoverErr := adapter.discoverBasicApplicationAID(
|
||||
ctx,
|
||||
deviceID,
|
||||
usimAIDPrefix,
|
||||
); discoverErr == nil {
|
||||
return discovered, "USIM", nil
|
||||
}
|
||||
|
||||
// AT+CUAD is optional on older EC20 firmware. CCHO still provides a
|
||||
// standards-based, evidence-bearing probe of the assigned USIM AID.
|
||||
@@ -1053,6 +1067,64 @@ func (adapter *EC20Adapter) discoverPreferredAKAApplication(
|
||||
return aidPrefix, application, nil
|
||||
}
|
||||
|
||||
func (adapter *EC20Adapter) discoverBasicApplicationAID(
|
||||
ctx context.Context,
|
||||
deviceID string,
|
||||
aidPrefix string,
|
||||
) (string, error) {
|
||||
selectFile := func(fileID uint16) error {
|
||||
apdu := []byte{
|
||||
0x00, 0xa4, 0x00, 0x04, 0x02,
|
||||
byte(fileID >> 8), byte(fileID), 0x00,
|
||||
}
|
||||
raw, err := adapter.transmitBasicAPDU(ctx, deviceID, apdu, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, status, err := splitAPDUStatus(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != 0x9000 {
|
||||
return fmt.Errorf("vocat: EC20 basic-channel SELECT returned %04X", status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := selectFile(0x3f00); err != nil {
|
||||
return "", fmt.Errorf("select EC20 MF for application discovery: %w", err)
|
||||
}
|
||||
if err := selectFile(efDIRFileID); err != nil {
|
||||
return "", fmt.Errorf("select EC20 EF_DIR for application discovery: %w", err)
|
||||
}
|
||||
for record := 1; record <= 32; record++ {
|
||||
raw, err := adapter.transmitBasicAPDU(
|
||||
ctx,
|
||||
deviceID,
|
||||
[]byte{0x00, 0xb2, byte(record), 0x04, 0x00},
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read EC20 EF_DIR record %d: %w", record, err)
|
||||
}
|
||||
body, status, err := splitAPDUStatus(raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if status == 0x6a83 || status == 0x9402 {
|
||||
break
|
||||
}
|
||||
if status != 0x9000 {
|
||||
continue
|
||||
}
|
||||
for _, candidate := range collectApplicationAIDs(body) {
|
||||
if strings.HasPrefix(candidate, aidPrefix) {
|
||||
return candidate, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", ErrEC20ApplicationAbsent
|
||||
}
|
||||
|
||||
func (adapter *EC20Adapter) openLogicalChannel(
|
||||
ctx context.Context,
|
||||
deviceID string,
|
||||
@@ -1174,8 +1246,17 @@ func (adapter *EC20Adapter) transmitBasicAPDU(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
collected = append(collected, body...)
|
||||
sw1 := byte(status >> 8)
|
||||
if sw1 == 0x6c {
|
||||
// The UICC knows the exact response length. Retry the original APDU
|
||||
// with the advised Le without retaining the procedure response.
|
||||
if len(current) < 5 {
|
||||
return nil, errors.New("vocat: EC20 APDU cannot apply corrected response length")
|
||||
}
|
||||
current[len(current)-1] = byte(status)
|
||||
continue
|
||||
}
|
||||
collected = append(collected, body...)
|
||||
if sw1 != 0x61 && sw1 != 0x9f {
|
||||
collected = append(collected, byte(status>>8), byte(status))
|
||||
return collected, nil
|
||||
|
||||
@@ -263,6 +263,59 @@ func TestEC20AdapterCSIMFallbackSupportsSuccessAndSynchronizationFailure(
|
||||
}
|
||||
}
|
||||
|
||||
func TestEC20AdapterDiscoversFullUSIMAIDFromEFDIRWhenCUADFails(t *testing.T) {
|
||||
t.Parallel()
|
||||
const fullAID = "A0000000871002FFFFFFFF8903020000"
|
||||
record := "61184F10" + fullAID + "50045553494D"
|
||||
encodedResponse := strings.ToUpper(hex.EncodeToString(successfulUSIMResponse()))
|
||||
var challenge AKAChallenge
|
||||
for index := range challenge.RAND {
|
||||
challenge.RAND[index] = byte(index)
|
||||
challenge.AUTN[index] = byte(0xf0 + index)
|
||||
}
|
||||
authAPDU := buildUSIMAuthenticateAPDU(challenge)
|
||||
authCommand := fmt.Sprintf(
|
||||
`AT+CSIM=%d,"%s"`,
|
||||
len(authAPDU)*2,
|
||||
strings.ToUpper(hex.EncodeToString(authAPDU)),
|
||||
)
|
||||
selectApplication := `AT+CSIM=42,"00A4040410` + fullAID + `"`
|
||||
transcript := &ec20Transcript{
|
||||
t: t,
|
||||
steps: append(
|
||||
identityTranscriptStepsWithoutEFAD("310280000000001"),
|
||||
[]ec20TranscriptStep{
|
||||
{command: "AT+CCID", lines: []string{"+CCID: 8944101234567890123"}},
|
||||
{command: "AT+CUAD", err: errors.New("+CME ERROR: 13"), final: "+CME ERROR: 13"},
|
||||
{command: `AT+CSIM=16,"00A40004023F0000"`, lines: []string{`+CSIM: 4,"9000"`}},
|
||||
{command: `AT+CSIM=16,"00A40004022F0000"`, lines: []string{`+CSIM: 4,"9000"`}},
|
||||
{command: `AT+CSIM=10,"00B2010400"`, lines: []string{`+CSIM: 4,"6C1A"`}},
|
||||
{command: `AT+CSIM=10,"00B201041A"`, lines: []string{fmt.Sprintf(`+CSIM: %d,"%s9000"`, len(record)+4, record)}},
|
||||
{command: `AT+CCHO="` + fullAID + `"`, err: errors.New("unsupported"), final: "ERROR"},
|
||||
{command: selectApplication, lines: []string{`+CSIM: 4,"9000"`}},
|
||||
{command: "AT+CCID", lines: []string{"+CCID: 8944101234567890123"}},
|
||||
{command: selectApplication, lines: []string{`+CSIM: 4,"9000"`}},
|
||||
{command: authCommand, sensitive: true, lines: []string{fmt.Sprintf(`+CSIM: %d,"%s"`, len(encodedResponse), encodedResponse)}},
|
||||
}...,
|
||||
),
|
||||
}
|
||||
adapter, err := NewEC20Adapter(transcript, EC20AdapterOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
identity, err := adapter.ReadIdentity(context.Background(), "ec20-1")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadIdentity: %v", err)
|
||||
}
|
||||
if _, err := adapter.CheckReady(context.Background(), identity); err != nil {
|
||||
t.Fatalf("CheckReady: %v", err)
|
||||
}
|
||||
if _, err := adapter.Authenticate(context.Background(), identity, challenge); err != nil {
|
||||
t.Fatalf("Authenticate: %v", err)
|
||||
}
|
||||
transcript.assertDone()
|
||||
}
|
||||
|
||||
func TestEC20AdapterLogicalChannelAuthenticateFollowsGetResponse(
|
||||
t *testing.T,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user