aboutsummaryrefslogtreecommitdiff
path: root/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java
blob: bb451fd00fb78d52761101bc548a65e9433fa4bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.libraries.entitlement.eapaka;

import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;

import com.android.libraries.entitlement.ServiceEntitlementException;

/**
 * Provide format to handle request/response SIM Authentication with GSM/3G security context.
 *
 * <p>Reference ETSI TS 131 102, Section 7.1.2.1 GSM/3G security context.
 */
class EapAkaSecurityContext {
    private static final String TAG = "ServiceEntitlement";

    private static final byte RESPONSE_TAG_SUCCESS = (byte) 0xDB;

    private boolean valid;

    /* Authentication result from SIM */
    private byte[] res;
    /* Cipher Key */
    private byte[] ck;
    /* Integrity Key */
    private byte[] ik;

    private EapAkaSecurityContext() {
    }

    /** Provide {@link EapAkaSecurityContext} from response data. */
    public static EapAkaSecurityContext from(String response)
            throws ServiceEntitlementException {
        EapAkaSecurityContext securityContext = new EapAkaSecurityContext();
        securityContext.parseResponseData(response);
        if (!securityContext.isValid()) {
            throw new ServiceEntitlementException("Invalid SIM EAP-AKA authentication response!");
        }
        return securityContext;
    }

    /**
     * Parses SIM EAP-AKA Authentication responsed data and returns valid {@link
     * EapAkaSecurityContext}
     * for successful data; otherwise, returns invalid.
     */
    void parseResponseData(String response) {
        if (TextUtils.isEmpty(response)) {
            Log.d(TAG, "parseResponseData but input empty data!");
            return;
        }

        try {
            byte[] data = Base64.decode(response, Base64.DEFAULT);
            Log.d(TAG, "decoded data length=" + data.length);

            if (data.length <= 2) {
                return;
            }

            int index = 0;

            // check tag
            if (data[index] != RESPONSE_TAG_SUCCESS) {
                Log.d(TAG, "Not successful data, tag=" + data[index]);
                return;
            }

            // Parse RES
            index++; // move to RES length byte
            res = parseTag(index, data);
            if (res == null) {
                Log.d(TAG, "Invalid data! can't parse RES!");
                return;
            }
            // Parse CK
            index += res.length + 1; // move to CK length byte
            ck = parseTag(index, data);
            if (ck == null) {
                Log.d(TAG, "Invalid data! can't parse CK!");
                return;
            }
            // Parse IK
            index += ck.length + 1; // move to IK length byte
            ik = parseTag(index, data);
            if (ik == null) {
                Log.d(TAG, "Invalid data! can't parse IK!");
                return;
            }

            valid = true;
        } catch (IllegalArgumentException illegalArgumentException) {
            Log.e(TAG, "Invalid base-64 content");
        }
    }


    private byte[] parseTag(int index, byte[] src) {
        // index at the length byte
        if (index >= src.length) {
            Log.d(TAG, "No length byte!");
            return null;
        }
        int length = src[index] & 0xff;
        if (index + length >= src.length) {
            Log.d(TAG, "Invalid data length!");
            return null;
        }
        index++; // move to first byte of tag value
        byte[] dest = new byte[length];
        System.arraycopy(src, index, dest, 0, length);

        return dest;
    }

    /** Returns {@code valid}. */
    boolean isValid() {
        return valid;
    }

    /** Returns {@code res}. */
    public byte[] getRes() {
        return res;
    }

    /** Returns {@code ck}. */
    public byte[] getCk() {
        return ck;
    }

    /** Returns {@code ik}. */
    public byte[] getIk() {
        return ik;
    }
}