• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.libraries.entitlement.eapaka;
18 
19 import static com.android.libraries.entitlement.ServiceEntitlementException.ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE;
20 
21 import static java.nio.charset.StandardCharsets.UTF_8;
22 
23 import android.util.Base64;
24 import android.util.Log;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.android.libraries.entitlement.ServiceEntitlementException;
29 
30 /**
31  * Provides format to handle request/response SIM Authentication with GSM/3G security context.
32  *
33  * <p>Reference ETSI TS 131 102, Section 7.1.2.1 GSM/3G security context.
34  */
35 class EapAkaSecurityContext {
36     private static final String TAG = "ServiceEntitlement";
37 
38     private static final byte RESPONSE_TAG_SUCCESS = (byte) 0xDB;
39     private static final byte RESPONSE_TAG_SYNC_FAILURE = (byte) 0xDC;
40 
41     private boolean mValid;
42 
43     // User response, populated on successful authentication
44     private byte[] mRes;
45     // Cipher Key, populated on successful authentication
46     private byte[] mCk;
47     // Integrity Key, populated on successful authentication
48     private byte[] mIk;
49     // AUTS, populated on synchronization failure
50     private byte[] mAuts;
51 
EapAkaSecurityContext()52     private EapAkaSecurityContext() {}
53 
54     /**
55      * Provide {@link EapAkaSecurityContext} from response data.
56      */
from(String response)57     public static EapAkaSecurityContext from(String response)
58             throws ServiceEntitlementException {
59         EapAkaSecurityContext securityContext = new EapAkaSecurityContext();
60         securityContext.parseResponseData(response);
61         if (!securityContext.isValid()) {
62             throw new ServiceEntitlementException(
63                 ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE,
64                 "Invalid SIM EAP-AKA authentication response!");
65         }
66         return securityContext;
67     }
68 
69     /**
70      * Parses SIM EAP-AKA Authentication responded data.
71      */
parseResponseData(String response)72     private void parseResponseData(String response) {
73         byte[] data = null;
74 
75         try {
76             data = Base64.decode(response.getBytes(UTF_8), Base64.DEFAULT);
77             Log.d(TAG, "Decoded response data length = " + data.length + " bytes");
78         } catch (IllegalArgumentException e) {
79             Log.e(TAG, "Response is not a valid base-64 content");
80             return;
81         }
82         if (data.length == 0) {
83             return;
84         }
85 
86         // Check tag, the initial byte
87         int index = 0;
88         if (data[index] == RESPONSE_TAG_SUCCESS) {
89             // Parse RES
90             index++; // move to RES length byte
91             mRes = parseTag(index, data);
92             if (mRes == null) {
93                 Log.d(TAG, "Invalid data: can't parse RES!");
94                 return;
95             }
96             // Parse CK
97             index += mRes.length + 1; // move to CK length byte
98             mCk = parseTag(index, data);
99             if (mCk == null) {
100                 Log.d(TAG, "Invalid data: can't parse CK!");
101                 return;
102             }
103             // Parse IK
104             index += mCk.length + 1; // move to IK length byte
105             mIk = parseTag(index, data);
106             if (mIk == null) {
107                 Log.d(TAG, "Invalid data: can't parse IK!");
108                 return;
109             }
110             mValid = true;
111         } else if (data[index] == RESPONSE_TAG_SYNC_FAILURE) {
112             // Parse AUTS
113             index++; // move to AUTS length byte
114             mAuts = parseTag(index, data);
115             if (mAuts == null) {
116                 Log.d(TAG, "Invalid data: can't parse AUTS!");
117                 return;
118             }
119             mValid = true;
120         } else {
121             Log.d(TAG, "Not a valid tag, tag=" + data[index]);
122             return;
123         }
124     }
125 
parseTag(int index, byte[] src)126     private byte[] parseTag(int index, byte[] src) {
127         // index at the length byte
128         if (index >= src.length) {
129             Log.d(TAG, "No length byte!");
130             return null;
131         }
132         int length = src[index] & 0xff;
133         if (index + length >= src.length) {
134             Log.d(TAG, "Invalid data length!");
135             return null;
136         }
137         index++; // move to first byte of tag value
138         byte[] dest = new byte[length];
139         System.arraycopy(src, index, dest, 0, length);
140 
141         return dest;
142     }
143 
isValid()144     private boolean isValid() {
145         return mValid;
146     }
147 
148     /**
149      * Returns RES, or {@code null} for a synchronization failure.
150      */
151     @Nullable
getRes()152     public byte[] getRes() {
153         return mRes;
154     }
155 
156     /**
157      * Returns CK, or {@code null} for a synchronization failure.
158      */
159     @Nullable
getCk()160     public byte[] getCk() {
161         return mCk;
162     }
163 
164     /**
165      * Returns IK, or {@code null} for a synchronization failure.
166      */
167     @Nullable
getIk()168     public byte[] getIk() {
169         return mIk;
170     }
171 
172     /**
173      * Returns AUTS, or {@code null} for a successful authentication.
174      */
175     @Nullable
getAuts()176     public byte[] getAuts() {
177         return mAuts;
178     }
179 }
180