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