• 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.internal.net.ipsec.ike.ike3gpp;
18 
19 import android.annotation.NonNull;
20 
21 import com.android.internal.net.ipsec.ike.message.IkeNotifyPayload;
22 
23 import java.nio.ByteBuffer;
24 
25 /**
26  * Ike3gppDeviceIdentityUtils contains functions needed to support 3GPP-specific DEVICE_IDENTITY
27  * payload.
28  */
29 public class Ike3gppDeviceIdentityUtils {
30     private static final int DEVICE_IDENTITY_PAYLOAD_LENGTH = 11;
31     // actual value to fill in length field of device identity payload
32     private static final short DEVICE_IDENTITY_PAYLOAD_LENGTH_FIELD_VAL = (short) 9;
33     private static final byte DEVICE_IDENTITY_TYPE_IMEI = (byte) 1;
34     private static final byte DEVICE_IDENTITY_TYPE_IMEISV = (byte) 2;
35     private static final int ENCODED_DEVICE_IDENTITY_LENGTH = 8;
36     private static final int IMEI_LENGTH = 15;
37     private static final int IMEISV_LENGTH = 16;
38 
39     /**
40      * Generate DEVICE_IDENTITY Notify payload.
41      *
42      * <p>This method formats the given device identity string to its payload format.
43      *
44      * @see TS 124 302, Fig/Table 8.2.9.2 for specification of DEVICE_IDENTITY Notify payload.
45      * @param deviceIdentity the device identity (IMEI/IMEISV)
46      * @return the formatted DEVICE_IDENTITY Notify payload data as a byte array.
47      */
generateDeviceIdentityPayload(@onNull String deviceIdentity)48     static IkeNotifyPayload generateDeviceIdentityPayload(@NonNull String deviceIdentity)
49             throws IllegalArgumentException {
50 
51         if (!isValidDeviceIdentity(deviceIdentity)) {
52             throw new IllegalArgumentException(
53                     "device identity should be a 15 or 16 digit numeric string");
54         }
55 
56         /*
57          * Actual payload format inside the Notify for DEVICE_IDENTITY:
58          *
59          *          BITS
60          * 7   6   5   4   3   2   1   0          OCTET
61          * ------------------------------
62          *          Length                         0-1
63          *
64          * ------------------------------
65          *         Identity type                    2
66          * ------------------------------
67          *
68          *
69          *
70          *         Identity Value                   3-10
71          *
72          *
73          *
74          * --------------------------------
75          *
76          *
77          * Identity Value field represents the device identity digits of the corresponding
78          * Identity type and is coded using BCD coding.
79          * For Identity Type 'IMEI' and 'IMEISV', Identity value digits are coded based on
80          * the IMEI and IMEISV structure defined in 3GPP TS 23.003 [3]. IMEI is 15 BCD
81          * digits and IMEISV is 16 BCD digits. Both IMEI and IMEISV are TBCD encoded. Bits 5
82          * to 8 of octet i+4 (where i represents the octet of the IMEI(SV) being encoded)
83          * encodes digit 2i, bits 1 to 4 of octet i+4 encodes digit 2i-1 (i.e the order of
84          * digits is swapped in each octet compared to the digit order defined in 3GPP TS
85          * 23.003 [2]). Digits are packed contiguously with no internal padding. For IMEI,
86          * bits 5 to 8 of the last octet shall be filled with an end mark coded as '1111'.
87          */
88 
89         ByteBuffer payloadData = ByteBuffer.allocate(DEVICE_IDENTITY_PAYLOAD_LENGTH);
90         payloadData.putShort(DEVICE_IDENTITY_PAYLOAD_LENGTH_FIELD_VAL);
91 
92         byte deviceIdentityType;
93         deviceIdentityType =
94                 (deviceIdentity.length() == IMEI_LENGTH)
95                         ? DEVICE_IDENTITY_TYPE_IMEI
96                         : DEVICE_IDENTITY_TYPE_IMEISV;
97         payloadData.put(deviceIdentityType);
98 
99         // pad IMEI
100         if (deviceIdentityType == DEVICE_IDENTITY_TYPE_IMEI) {
101             deviceIdentity += "0";
102         }
103 
104         byte[] encodedIdentity = new byte[ENCODED_DEVICE_IDENTITY_LENGTH];
105         for (int i = 0, j = 0; i + 1 < 16; i += 2, j++) {
106 
107             // nibble1 and nibble2 represent the nibbles in the final swapped BCD encoding
108             byte nibble1 = (byte) Character.getNumericValue(deviceIdentity.charAt(i + 1));
109             byte nibble2 = (byte) Character.getNumericValue(deviceIdentity.charAt(i));
110 
111             if (deviceIdentityType == DEVICE_IDENTITY_TYPE_IMEI && j == 7) {
112                 // For IMEI, bits 5 to 8 of the last octet shall be filled with
113                 // an end mark coded as '1111'.
114                 encodedIdentity[j] = (byte) ((0xF << 4) | nibble2);
115             } else {
116                 encodedIdentity[j] = (byte) ((nibble1 << 4) | nibble2);
117             }
118         }
119 
120         payloadData.put(encodedIdentity);
121 
122         return new IkeNotifyPayload(
123                 Ike3gppExtensionExchange.NOTIFY_TYPE_DEVICE_IDENTITY, payloadData.array());
124     }
125 
isValidDeviceIdentity(String deviceIdentity)126     public static boolean isValidDeviceIdentity(String deviceIdentity) {
127         if (deviceIdentity != null) {
128             int deviceIdentityLen = deviceIdentity.length();
129             if ((deviceIdentityLen == IMEI_LENGTH || deviceIdentityLen == IMEISV_LENGTH)
130                     && (deviceIdentity.matches("[0-9]+"))) {
131                 return true;
132             }
133         }
134         return false;
135     }
136 }
137