1 // Copyright 2017 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.AccessesPartialKey; 20 import com.google.crypto.tink.InsecureSecretKeyAccess; 21 import com.google.crypto.tink.config.internal.TinkFipsUtil; 22 import com.google.crypto.tink.mac.internal.AesUtil; 23 import com.google.crypto.tink.prf.AesCmacPrfKey; 24 import com.google.crypto.tink.prf.Prf; 25 import com.google.errorprone.annotations.Immutable; 26 import java.security.GeneralSecurityException; 27 import java.security.InvalidAlgorithmParameterException; 28 import java.util.Arrays; 29 import javax.crypto.Cipher; 30 import javax.crypto.SecretKey; 31 import javax.crypto.spec.SecretKeySpec; 32 33 /** 34 * An implementation of CMAC following <a href="https://tools.ietf.org/html/rfc4493">RFC 4493</a>. 35 */ 36 @Immutable 37 @AccessesPartialKey 38 public final class PrfAesCmac implements Prf { 39 public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS = 40 TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS; 41 42 @SuppressWarnings("Immutable") 43 private final SecretKey keySpec; 44 45 @SuppressWarnings("Immutable") 46 private byte[] subKey1; 47 48 @SuppressWarnings("Immutable") 49 private byte[] subKey2; 50 51 private static final ThreadLocal<Cipher> localAesCipher = 52 new ThreadLocal<Cipher>() { 53 @Override 54 protected Cipher initialValue() { 55 try { 56 return EngineFactory.CIPHER.getInstance("AES/ECB/NoPadding"); 57 } catch (GeneralSecurityException ex) { 58 throw new IllegalStateException(ex); 59 } 60 } 61 }; 62 instance()63 private static Cipher instance() throws GeneralSecurityException { 64 if (!FIPS.isCompatible()) { 65 throw new GeneralSecurityException("Can not use AES-CMAC in FIPS-mode."); 66 } 67 return localAesCipher.get(); 68 } 69 PrfAesCmac(final byte[] key)70 public PrfAesCmac(final byte[] key) throws GeneralSecurityException { 71 Validators.validateAesKeySize(key.length); 72 73 keySpec = new SecretKeySpec(key, "AES"); 74 generateSubKeys(); 75 } 76 create(AesCmacPrfKey key)77 public static Prf create(AesCmacPrfKey key) throws GeneralSecurityException { 78 return new PrfAesCmac(key.getKeyBytes().toByteArray(InsecureSecretKeyAccess.get())); 79 } 80 81 // Only visible for testing. calcN(int dataLength)82 static int calcN(int dataLength) { 83 if (dataLength == 0) { 84 return 1; 85 } 86 return (dataLength - 1) / AesUtil.BLOCK_SIZE + 1; 87 } 88 xorBlock(final byte[] x, final byte[] y, int offsetY, byte[] output)89 private static void xorBlock(final byte[] x, final byte[] y, int offsetY, byte[] output) { 90 for (int i = 0; i < AesUtil.BLOCK_SIZE; i++) { 91 output[i] = (byte) (x[i] ^ y[i + offsetY]); 92 } 93 } 94 95 // https://tools.ietf.org/html/rfc4493#section-2.4 96 @Override compute(final byte[] data, int outputLength)97 public byte[] compute(final byte[] data, int outputLength) throws GeneralSecurityException { 98 if (outputLength > AesUtil.BLOCK_SIZE) { 99 throw new InvalidAlgorithmParameterException( 100 "outputLength too large, max is " + AesUtil.BLOCK_SIZE + " bytes"); 101 } 102 Cipher aes = instance(); 103 aes.init(Cipher.ENCRYPT_MODE, keySpec); 104 105 // n is the number of blocks (including partial blocks) into which the data 106 // is divided. Empty data is divided into 1 empty block. 107 // Step 2: n = ceil(length / blocksize) 108 // TODO(b/68969256): Adding a test that computes a CMAC of length 2**31-1. 109 int n = calcN(data.length); 110 111 // Step 3 112 boolean flag = (n * AesUtil.BLOCK_SIZE == data.length); 113 114 // Step 4 115 byte[] mLast; 116 if (flag) { 117 mLast = Bytes.xor(data, (n - 1) * AesUtil.BLOCK_SIZE, subKey1, 0, AesUtil.BLOCK_SIZE); 118 } else { 119 mLast = 120 Bytes.xor( 121 AesUtil.cmacPad(Arrays.copyOfRange(data, (n - 1) * AesUtil.BLOCK_SIZE, data.length)), 122 subKey2); 123 } 124 125 // Step 5 126 byte[] x = new byte[AesUtil.BLOCK_SIZE]; 127 128 // Step 6 129 byte[] y = new byte[AesUtil.BLOCK_SIZE]; 130 for (int i = 0; i < n - 1; i++) { 131 xorBlock(x, data, i * AesUtil.BLOCK_SIZE, /* output= */ y); 132 int written = aes.doFinal(y, 0, AesUtil.BLOCK_SIZE, /* output= */ x); 133 if (written != AesUtil.BLOCK_SIZE) { 134 throw new IllegalStateException("Cipher didn't write full block"); 135 } 136 } 137 xorBlock(x, mLast, 0, /* output= */ y); 138 139 // Step 7 140 int written = aes.doFinal(y, 0, AesUtil.BLOCK_SIZE, /* output= */ x); 141 if (written != AesUtil.BLOCK_SIZE) { 142 throw new IllegalStateException("Cipher didn't write full block"); 143 } 144 if (x.length == outputLength) { 145 return x; 146 } 147 return Arrays.copyOf(x, outputLength); 148 } 149 150 // https://tools.ietf.org/html/rfc4493#section-2.3 generateSubKeys()151 private void generateSubKeys() throws GeneralSecurityException { 152 Cipher aes = instance(); 153 aes.init(Cipher.ENCRYPT_MODE, keySpec); 154 byte[] zeroes = new byte[AesUtil.BLOCK_SIZE]; 155 byte[] l = aes.doFinal(zeroes); 156 subKey1 = AesUtil.dbl(l); 157 subKey2 = AesUtil.dbl(subKey1); 158 } 159 } 160