• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.crypto;
18 
19 import android.annotation.Nullable;
20 import android.net.IpSecAlgorithm;
21 import android.net.ipsec.ike.SaProposal;
22 import android.util.SparseArray;
23 
24 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
25 
26 import java.security.NoSuchAlgorithmException;
27 import java.security.SecureRandom;
28 
29 import javax.crypto.Cipher;
30 import javax.crypto.NoSuchPaddingException;
31 
32 /**
33  * IkeCipher contains common information of normal and combined mode encryption algorithms.
34  *
35  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange
36  *     Protocol Version 2 (IKEv2)</a>
37  */
38 public abstract class IkeCipher extends IkeCrypto {
39     private static final int KEY_LEN_3DES = 24;
40     private static final int KEY_LEN_CHACHA20_POLY1305 = 32;
41 
42     private static final int IV_LEN_3DES = 8;
43     private static final int IV_LEN_AES_CBC = 16;
44     private static final int IV_LEN_AES_CTR = 8;
45     private static final int IV_LEN_AES_GCM = 8;
46     private static final int IV_LEN_CHACHA20_POLY1305 = 8;
47 
48     private static final int SALT_LEN_AES_GCM = 4;
49     private static final int SALT_LEN_AES_CTR = 4;
50     private static final int SALT_LEN_AES_CHACHA20_POLY1305 = 4;
51 
52     private static final int BLOCK_SIZE_CHACHA_POLY = 4;
53 
54     protected static final int SALT_LEN_NOT_INCLUDED = 0;
55     protected static final int BLOCK_SIZE_NOT_SPECIFIED = 0;
56 
57     // Map IKE algorithm numbers to IPsec algorithm names
58     private static final SparseArray<String> IKE_ALGO_TO_IPSEC_ALGO;
59 
60     static {
61         IKE_ALGO_TO_IPSEC_ALGO = new SparseArray<>();
IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, IpSecAlgorithm.CRYPT_AES_CBC)62         IKE_ALGO_TO_IPSEC_ALGO.put(
63                 SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, IpSecAlgorithm.CRYPT_AES_CBC);
IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_CTR, IpSecAlgorithm.CRYPT_AES_CTR)64         IKE_ALGO_TO_IPSEC_ALGO.put(
65                 SaProposal.ENCRYPTION_ALGORITHM_AES_CTR, IpSecAlgorithm.CRYPT_AES_CTR);
IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)66         IKE_ALGO_TO_IPSEC_ALGO.put(
67                 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)68         IKE_ALGO_TO_IPSEC_ALGO.put(
69                 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12, IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, IpSecAlgorithm.AUTH_CRYPT_AES_GCM)70         IKE_ALGO_TO_IPSEC_ALGO.put(
71                 SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16, IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
IKE_ALGO_TO_IPSEC_ALGO.put( SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305)72         IKE_ALGO_TO_IPSEC_ALGO.put(
73                 SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305,
74                 IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305);
75     }
76 
77     private final boolean mIsAead;
78     private final int mIvLen;
79     private final int mBlockSize;
80 
81     protected final int mSaltLen;
82     protected final Cipher mCipher;
83 
IkeCipher( int algorithmId, int keyLength, int ivLength, String algorithmName, boolean isAead, int saltLen, int blockSize)84     protected IkeCipher(
85             int algorithmId,
86             int keyLength,
87             int ivLength,
88             String algorithmName,
89             boolean isAead,
90             int saltLen,
91             int blockSize) {
92         super(algorithmId, keyLength, algorithmName);
93         mIvLen = ivLength;
94         mIsAead = isAead;
95         mSaltLen = saltLen;
96 
97         try {
98             mCipher = Cipher.getInstance(getAlgorithmName());
99             mBlockSize = blockSize == BLOCK_SIZE_NOT_SPECIFIED ? mCipher.getBlockSize() : blockSize;
100         } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
101             throw new IllegalArgumentException("Failed to construct " + getTypeString(), e);
102         }
103     }
104 
105     /**
106      * Contruct an instance of IkeCipher.
107      *
108      * @param encryptionTransform the valid negotiated EncryptionTransform.
109      * @return an instance of IkeCipher.
110      */
create(EncryptionTransform encryptionTransform)111     public static IkeCipher create(EncryptionTransform encryptionTransform) {
112         int algorithmId = encryptionTransform.id;
113 
114         // Use specifiedKeyLength for algorithms with variable key length. Since
115         // specifiedKeyLength are encoded in bits, it needs to be converted to bytes.
116         switch (algorithmId) {
117             case SaProposal.ENCRYPTION_ALGORITHM_3DES:
118                 return new IkeNormalModeCipher(
119                         algorithmId, KEY_LEN_3DES, IV_LEN_3DES, "DESede/CBC/NoPadding");
120             case SaProposal.ENCRYPTION_ALGORITHM_AES_CBC:
121                 return new IkeNormalModeCipher(
122                         algorithmId,
123                         encryptionTransform.getSpecifiedKeyLength() / 8,
124                         IV_LEN_AES_CBC,
125                         "AES/CBC/NoPadding");
126             case SaProposal.ENCRYPTION_ALGORITHM_AES_CTR:
127                 return new IkeNormalModeCipher(
128                         algorithmId,
129                         encryptionTransform.getSpecifiedKeyLength() / 8,
130                         IV_LEN_AES_CTR,
131                         "AES/CTR/NoPadding",
132                         SALT_LEN_AES_CTR);
133             case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8:
134                 // Fall through
135             case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12:
136                 // Fall through
137             case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16:
138                 // Fall through
139                 return new IkeCombinedModeCipher(
140                         algorithmId,
141                         encryptionTransform.getSpecifiedKeyLength() / 8,
142                         IV_LEN_AES_GCM,
143                         "AES/GCM/NoPadding",
144                         SALT_LEN_AES_GCM);
145             case SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305:
146                 return new IkeCombinedModeCipher(
147                         algorithmId,
148                         KEY_LEN_CHACHA20_POLY1305,
149                         IV_LEN_CHACHA20_POLY1305,
150                         "ChaCha20/Poly1305/NoPadding",
151                         SALT_LEN_AES_CHACHA20_POLY1305,
152                         BLOCK_SIZE_CHACHA_POLY);
153             default:
154                 throw new IllegalArgumentException(
155                         "Unrecognized Encryption Algorithm ID: " + algorithmId);
156         }
157     }
158 
159     /**
160      * Check if this encryption algorithm is a combined-mode/AEAD algorithm.
161      *
162      * @return if this encryption algorithm is a combined-mode/AEAD algorithm.
163      */
isAead()164     public boolean isAead() {
165         return mIsAead;
166     }
167 
168     /**
169      * Get the block size (in bytes).
170      *
171      * @return the block size (in bytes).
172      */
getBlockSize()173     public int getBlockSize() {
174         return mBlockSize;
175     }
176 
177     /**
178      * Get initialization vector (IV) length.
179      *
180      * @return the IV length.
181      */
getIvLen()182     public int getIvLen() {
183         return mIvLen;
184     }
185 
186     /**
187      * Generate initialization vector (IV).
188      *
189      * @return the initialization vector (IV).
190      */
generateIv()191     public byte[] generateIv() {
192         byte[] iv = new byte[getIvLen()];
193         new SecureRandom().nextBytes(iv);
194         return iv;
195     }
196 
validateKeyLenOrThrow(byte[] key)197     protected void validateKeyLenOrThrow(byte[] key) {
198         if (key.length != getKeyLength()) {
199             throw new IllegalArgumentException(
200                     "Expected key with length of : "
201                             + getKeyLength()
202                             + " Received key with length of : "
203                             + key.length);
204         }
205     }
206 
207     @Override
getKeyLength()208     public int getKeyLength() {
209         return super.getKeyLength() + mSaltLen;
210     }
211 
212     /**
213      * Returns the IPsec algorithm name defined in {@link IpSecAlgorithm} given the IKE algorithm
214      * ID.
215      *
216      * <p>Returns null if there is no corresponding IPsec algorithm given the IKE algorithm ID.
217      */
218     @Nullable
getIpSecAlgorithmName(int ikeAlgoId)219     public static String getIpSecAlgorithmName(int ikeAlgoId) {
220         return IKE_ALGO_TO_IPSEC_ALGO.get(ikeAlgoId);
221     }
222 
buildIpSecAlgorithmWithKeyImpl(byte[] key)223     protected abstract IpSecAlgorithm buildIpSecAlgorithmWithKeyImpl(byte[] key);
224 
225     /**
226      * Build IpSecAlgorithm from this IkeCipher.
227      *
228      * <p>Build IpSecAlgorithm that represents the same encryption algorithm with this IkeCipher
229      * instance with provided encryption key.
230      *
231      * @param key the encryption key in byte array.
232      * @return the IpSecAlgorithm.
233      */
buildIpSecAlgorithmWithKey(byte[] key)234     public IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key) {
235         validateKeyLenOrThrow(key);
236         if (getIpSecAlgorithmName(getAlgorithmId()) == null) {
237             throw new IllegalStateException(
238                     "Unsupported algorithm " + getAlgorithmId() + " in IPsec");
239         }
240         return buildIpSecAlgorithmWithKeyImpl(key);
241     }
242 
243     /**
244      * Returns algorithm type as a String.
245      *
246      * @return the algorithm type as a String.
247      */
248     @Override
getTypeString()249     public String getTypeString() {
250         return "Encryption Algorithm";
251     }
252 }
253