1 // Copyright 2018 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.subtle; 18 19 import com.google.crypto.tink.KeyWrap; 20 import java.security.GeneralSecurityException; 21 import java.util.Arrays; 22 import javax.crypto.BadPaddingException; 23 import javax.crypto.Cipher; 24 import javax.crypto.SecretKey; 25 import javax.crypto.spec.SecretKeySpec; 26 27 /** 28 * Implements the key wrapping primitive KWP defined in NIST SP 800 38f. The same encryption mode is 29 * also defined in RFC 5649. The NIST document is used here as a primary reference, since it 30 * contains a security analysis and further recommendations. In particular, Section 8 of NIST SP 800 31 * 38f suggests that the allowed key sizes may be restricted. The implementation in this class 32 * requires that the key sizes are in the range MIN_WRAP_KEY_SIZE and MAX_WRAP_KEY_SIZE. 33 * 34 * <p>The minimum of 16 bytes has been chosen, because 128 bit keys are the smallest key sizes used 35 * in tink. Additionally, wrapping short keys with KWP does not use the function W and hence 36 * prevents using security arguments based on the assumption that W is strong pseudorandom. (I.e. 37 * one consequence of using a strong pseudorandom permutation as an underlying function is that 38 * leaking partial information about decrypted bytes is not useful for an attack.) 39 * 40 * <p>The upper bound for the key size is somewhat arbitrary. Setting an upper bound is motivated by 41 * the analysis in section A.4 of NIST SP 800 38f: forgeries of long messages is simpler than 42 * forgeries of short message. 43 * 44 * @deprecated Tink does not support KeyWrap anymore. This implementation was fallback code for old 45 * providers that did not implement KWP. It implements the same functionality as {@code 46 * Cipher.getInstance("AESWRAPPAD");}. Some provider use a different algorithm name: {@code 47 * Cipher.getInstance("AES/KWP/NoPadding");}. 48 */ 49 @Deprecated 50 public class Kwp implements KeyWrap { 51 private final SecretKey aesKey; 52 53 static final int MIN_WRAP_KEY_SIZE = 16; 54 static final int MAX_WRAP_KEY_SIZE = 4096; 55 static final int ROUNDS = 6; 56 static final byte[] PREFIX = new byte[]{(byte) 0xa6, (byte) 0x59, (byte) 0x59, (byte) 0xa6}; 57 58 /** 59 * Construct a new Instance for KWP. 60 * @param key the wrapping key. This is an AES key. 61 * Supported key sizes are 128 and 256 bits. 62 */ Kwp(final byte[] key)63 public Kwp(final byte[] key) throws GeneralSecurityException { 64 if (key.length != 16 && key.length != 32) { 65 throw new GeneralSecurityException("Unsupported key length"); 66 } 67 aesKey = new SecretKeySpec(key, "AES"); 68 } 69 70 /** 71 * Returns the size of a wrapped key for a given input size. 72 * @param inputSize the size of the key to wrap in bytes. 73 */ wrappingSize(int inputSize)74 private int wrappingSize(int inputSize) { 75 int paddingSize = 7 - (inputSize + 7) % 8; 76 return inputSize + paddingSize + 8; 77 } 78 79 /** 80 * Computes the pseudorandom permutation W over the IV 81 * concatenated with zero padded key material. 82 * @param iv an IV of size 8. 83 * @param key the key to wrap. 84 * The pseudorandom permutation W is only defined for 85 * inputs with a size that is a multiple of 8 bytes and 86 * that is at least 24 bytes long. Hence computeW is undefined 87 * for keys of size 8 bytes or shorter. 88 */ computeW(final byte[] iv, final byte[] key)89 private byte[] computeW(final byte[] iv, final byte[] key) 90 throws GeneralSecurityException { 91 // Checks the parameter sizes for which W is defined. 92 // Note, that the caller ensures stricter limits. 93 if (key.length <= 8 || key.length > Integer.MAX_VALUE - 16 || iv.length != 8) { 94 throw new GeneralSecurityException("computeW called with invalid parameters"); 95 } 96 byte[] data = new byte[wrappingSize(key.length)]; 97 System.arraycopy(iv, 0, data, 0, iv.length); 98 System.arraycopy(key, 0, data, 8, key.length); 99 int blocks = data.length / 8 - 1; 100 Cipher aes = EngineFactory.CIPHER.getInstance("AES/ECB/NoPadding"); 101 aes.init(Cipher.ENCRYPT_MODE, aesKey); 102 byte[] block = new byte[16]; 103 System.arraycopy(data, 0, block, 0, 8); 104 for (int i = 0; i < ROUNDS; i++) { 105 for (int j = 0; j < blocks; j++) { 106 System.arraycopy(data, 8 * (j + 1), block, 8, 8); 107 int length = aes.doFinal(block, 0, 16, block); 108 assert length == 16; 109 // xor the round constant in bigendian order to the left half of block. 110 int roundConst = i * blocks + j + 1; 111 for (int b = 0; b < 4; b++) { 112 block[7 - b] ^= (byte) (roundConst & 0xff); 113 roundConst >>>= 8; 114 } 115 System.arraycopy(block, 8, data, 8 * (j + 1), 8); 116 } 117 } 118 System.arraycopy(block, 0, data, 0, 8); 119 return data; 120 } 121 122 /** 123 * Compute the inverse of the pseudorandom permutation W. 124 * @param wrapped the input data to invert. This is the wrapped key. 125 * @return the concatenation of the IV followed by a potentially 126 * zero padded key. 127 * invertW does not perform an integrity check. 128 */ invertW(final byte[] wrapped)129 private byte[] invertW(final byte[] wrapped) throws GeneralSecurityException { 130 // Checks the input size for which invertW is defined. 131 // The caller ensures stricter limits 132 if (wrapped.length < 24 || wrapped.length % 8 != 0) { 133 throw new GeneralSecurityException("Incorrect data size"); 134 } 135 byte[] data = Arrays.copyOf(wrapped, wrapped.length); 136 int blocks = data.length / 8 - 1; 137 Cipher aes = EngineFactory.CIPHER.getInstance("AES/ECB/NoPadding"); 138 aes.init(Cipher.DECRYPT_MODE, aesKey); 139 byte[] block = new byte[16]; 140 System.arraycopy(data, 0, block, 0, 8); 141 for (int i = ROUNDS - 1; i >= 0; i--) { 142 for (int j = blocks - 1; j >= 0; j--) { 143 System.arraycopy(data, 8 * (j + 1), block, 8, 8); 144 // xor the round constant in bigendian order to the left half of block. 145 int roundConst = i * blocks + j + 1; 146 for (int b = 0; b < 4; b++) { 147 block[7 - b] ^= (byte) (roundConst & 0xff); 148 roundConst >>>= 8; 149 } 150 151 int length = aes.doFinal(block, 0, 16, block); 152 assert length == 16; 153 System.arraycopy(block, 8, data, 8 * (j + 1), 8); 154 } 155 } 156 System.arraycopy(block, 0, data, 0, 8); 157 return data; 158 } 159 160 /** 161 * Wraps some key material {@code data}. 162 * 163 * @param data the key to wrap. 164 * @return the wrapped key 165 */ 166 @Override wrap(final byte[] data)167 public byte[] wrap(final byte[] data) throws GeneralSecurityException { 168 if (data.length < MIN_WRAP_KEY_SIZE) { 169 throw new GeneralSecurityException("Key size of key to wrap too small"); 170 } 171 if (data.length > MAX_WRAP_KEY_SIZE) { 172 throw new GeneralSecurityException("Key size of key to wrap too large"); 173 } 174 byte[] iv = new byte[8]; 175 System.arraycopy(PREFIX, 0, iv, 0, PREFIX.length); 176 for (int i = 0; i < 4; i++) { 177 iv[4 + i] = (byte) ((data.length >> (8 * (3 - i))) & 0xff); 178 } 179 return computeW(iv, data); 180 } 181 182 /** 183 * Unwraps a wrapped key. 184 * 185 * @throws GeneralSecurityException if {@code data} fails the integrity check. 186 */ 187 @Override unwrap(final byte[] data)188 public byte[] unwrap(final byte[] data) throws GeneralSecurityException { 189 if (data.length < wrappingSize(MIN_WRAP_KEY_SIZE)) { 190 throw new GeneralSecurityException("Wrapped key size is too small"); 191 } 192 if (data.length > wrappingSize(MAX_WRAP_KEY_SIZE)) { 193 throw new GeneralSecurityException("Wrapped key size is too large"); 194 } 195 if (data.length % 8 != 0) { 196 throw new GeneralSecurityException( 197 "Wrapped key size must be a multiple of 8 bytes"); 198 } 199 byte[] unwrapped = invertW(data); 200 // Check the padding. 201 // W has been designed to be a strong pseudorandom permutation. 202 // Hence leaking any amount of information about improperly padded keys 203 // would not be a vulnerability. This means that here we don't have to go to 204 // some extra length to assure that the code is constant time. 205 boolean ok = true; 206 for (int i = 0; i < 4; i++) { 207 if (PREFIX[i] != unwrapped[i]) { 208 ok = false; 209 } 210 } 211 int encodedSize = 0; 212 for (int i = 4; i < 8; i++) { 213 encodedSize = (encodedSize << 8) + (unwrapped[i] & 0xff); 214 } 215 if (wrappingSize(encodedSize) != unwrapped.length) { 216 ok = false; 217 } else { 218 for (int j = 8 + encodedSize; j < unwrapped.length; j++) { 219 if (unwrapped[j] != 0) { 220 ok = false; 221 } 222 } 223 } 224 if (ok) { 225 return Arrays.copyOfRange(unwrapped, 8, 8 + encodedSize); 226 } else { 227 throw new BadPaddingException("Invalid padding"); 228 } 229 } 230 } 231