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.net.IpSecAlgorithm;
20 import android.net.ipsec.ike.SaProposal;
21 
22 import com.android.internal.net.ipsec.ike.message.IkeSaPayload.EncryptionTransform;
23 
24 import java.security.NoSuchAlgorithmException;
25 import java.security.SecureRandom;
26 
27 import javax.crypto.Cipher;
28 import javax.crypto.NoSuchPaddingException;
29 
30 /**
31  * IkeCipher contains common information of normal and combined mode encryption algorithms.
32  *
33  * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange
34  *     Protocol Version 2 (IKEv2)</a>
35  */
36 public abstract class IkeCipher extends IkeCrypto {
37     private static final int KEY_LEN_3DES = 24;
38 
39     private static final int IV_LEN_3DES = 8;
40     private static final int IV_LEN_AES_CBC = 16;
41     private static final int IV_LEN_AES_GCM = 8;
42 
43     private final boolean mIsAead;
44     private final int mIvLen;
45 
46     protected final Cipher mCipher;
47 
IkeCipher( int algorithmId, int keyLength, int ivLength, String algorithmName, boolean isAead)48     protected IkeCipher(
49             int algorithmId, int keyLength, int ivLength, String algorithmName, boolean isAead) {
50         super(algorithmId, keyLength, algorithmName);
51         mIvLen = ivLength;
52         mIsAead = isAead;
53 
54         try {
55             mCipher = Cipher.getInstance(getAlgorithmName());
56         } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
57             throw new IllegalArgumentException("Failed to construct " + getTypeString(), e);
58         }
59     }
60 
61     /**
62      * Contruct an instance of IkeCipher.
63      *
64      * @param encryptionTransform the valid negotiated EncryptionTransform.
65      * @return an instance of IkeCipher.
66      */
create(EncryptionTransform encryptionTransform)67     public static IkeCipher create(EncryptionTransform encryptionTransform) {
68         int algorithmId = encryptionTransform.id;
69 
70         // Use specifiedKeyLength for algorithms with variable key length. Since
71         // specifiedKeyLength are encoded in bits, it needs to be converted to bytes.
72         switch (algorithmId) {
73             case SaProposal.ENCRYPTION_ALGORITHM_3DES:
74                 return new IkeNormalModeCipher(
75                         algorithmId, KEY_LEN_3DES, IV_LEN_3DES, "DESede/CBC/NoPadding");
76             case SaProposal.ENCRYPTION_ALGORITHM_AES_CBC:
77                 return new IkeNormalModeCipher(
78                         algorithmId,
79                         encryptionTransform.getSpecifiedKeyLength() / 8,
80                         IV_LEN_AES_CBC,
81                         "AES/CBC/NoPadding");
82             case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8:
83                 // Fall through
84             case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12:
85                 // Fall through
86             case SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16:
87                 // Fall through
88                 return new IkeCombinedModeCipher(
89                         algorithmId,
90                         encryptionTransform.getSpecifiedKeyLength() / 8,
91                         IV_LEN_AES_GCM,
92                         "AES/GCM/NoPadding");
93             default:
94                 throw new IllegalArgumentException(
95                         "Unrecognized Encryption Algorithm ID: " + algorithmId);
96         }
97     }
98 
99     /**
100      * Check if this encryption algorithm is a combined-mode/AEAD algorithm.
101      *
102      * @return if this encryption algorithm is a combined-mode/AEAD algorithm.
103      */
isAead()104     public boolean isAead() {
105         return mIsAead;
106     }
107 
108     /**
109      * Get the block size (in bytes).
110      *
111      * @return the block size (in bytes).
112      */
getBlockSize()113     public int getBlockSize() {
114         // Currently all supported encryption algorithms are block ciphers. So the return value will
115         // not be zero.
116         return mCipher.getBlockSize();
117     }
118 
119     /**
120      * Get initialization vector (IV) length.
121      *
122      * @return the IV length.
123      */
getIvLen()124     public int getIvLen() {
125         return mIvLen;
126     }
127 
128     /**
129      * Generate initialization vector (IV).
130      *
131      * @return the initialization vector (IV).
132      */
generateIv()133     public byte[] generateIv() {
134         byte[] iv = new byte[getIvLen()];
135         new SecureRandom().nextBytes(iv);
136         return iv;
137     }
138 
validateKeyLenOrThrow(byte[] key)139     protected void validateKeyLenOrThrow(byte[] key) {
140         if (key.length != getKeyLength()) {
141             throw new IllegalArgumentException(
142                     "Expected key with length of : "
143                             + getKeyLength()
144                             + " Received key with length of : "
145                             + key.length);
146         }
147     }
148 
149     /**
150      * Build IpSecAlgorithm from this IkeCipher.
151      *
152      * <p>Build IpSecAlgorithm that represents the same encryption algorithm with this IkeCipher
153      * instance with provided encryption key.
154      *
155      * @param key the encryption key in byte array.
156      * @return the IpSecAlgorithm.
157      */
buildIpSecAlgorithmWithKey(byte[] key)158     public abstract IpSecAlgorithm buildIpSecAlgorithmWithKey(byte[] key);
159 
160     /**
161      * Returns algorithm type as a String.
162      *
163      * @return the algorithm type as a String.
164      */
165     @Override
getTypeString()166     public String getTypeString() {
167         return "Encryption Algorithm";
168     }
169 }
170