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.prf.HmacPrfKey; 23 import com.google.crypto.tink.prf.Prf; 24 import com.google.errorprone.annotations.Immutable; 25 import java.security.GeneralSecurityException; 26 import java.security.InvalidAlgorithmParameterException; 27 import java.security.NoSuchAlgorithmException; 28 import java.util.Arrays; 29 import javax.crypto.Mac; 30 import javax.crypto.spec.SecretKeySpec; 31 32 /** {@link Prf} implementation using JCE. */ 33 @Immutable 34 @AccessesPartialKey 35 public final class PrfHmacJce implements Prf { 36 public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS = 37 TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO; 38 39 static final int MIN_KEY_SIZE_IN_BYTES = 16; 40 41 // We do not mutate the underlying mac and it is bound to the containing PrfHmacJce instance. 42 @SuppressWarnings({"Immutable", "ThreadLocalUsage"}) 43 private final ThreadLocal<Mac> localMac = 44 new ThreadLocal<Mac>() { 45 @Override 46 protected Mac initialValue() { 47 try { 48 Mac mac = EngineFactory.MAC.getInstance(algorithm); 49 mac.init(key); 50 return mac; 51 } catch (GeneralSecurityException ex) { 52 throw new IllegalStateException(ex); 53 } 54 } 55 }; 56 57 private final String algorithm; 58 @SuppressWarnings("Immutable") // We do not mutate the key. 59 private final java.security.Key key; 60 61 private final int maxOutputLength; 62 PrfHmacJce(String algorithm, java.security.Key key)63 public PrfHmacJce(String algorithm, java.security.Key key) throws GeneralSecurityException { 64 if (!FIPS.isCompatible()) { 65 throw new GeneralSecurityException( 66 "Can not use HMAC in FIPS-mode, as BoringCrypto module is not available."); 67 } 68 69 this.algorithm = algorithm; 70 this.key = key; 71 if (key.getEncoded().length < MIN_KEY_SIZE_IN_BYTES) { 72 throw new InvalidAlgorithmParameterException( 73 "key size too small, need at least " + MIN_KEY_SIZE_IN_BYTES + " bytes"); 74 } 75 76 switch (algorithm) { 77 case "HMACSHA1": 78 maxOutputLength = 20; 79 break; 80 case "HMACSHA224": 81 maxOutputLength = 28; 82 break; 83 case "HMACSHA256": 84 maxOutputLength = 32; 85 break; 86 case "HMACSHA384": 87 maxOutputLength = 48; 88 break; 89 case "HMACSHA512": 90 maxOutputLength = 64; 91 break; 92 default: 93 throw new NoSuchAlgorithmException("unknown Hmac algorithm: " + algorithm); 94 } 95 96 // Initialize the current threads mac, mostly to fail fast if anything is wrong. 97 localMac.get(); 98 } 99 100 /** Given an HmacPrfKey, returns an instance of the Prf interface. */ create(HmacPrfKey key)101 public static Prf create(HmacPrfKey key) throws GeneralSecurityException { 102 return new PrfHmacJce( 103 "HMAC" + key.getParameters().getHashType(), 104 new SecretKeySpec(key.getKeyBytes().toByteArray(InsecureSecretKeyAccess.get()), "HMAC")); 105 } 106 107 @Override compute(byte[] data, int outputLength)108 public byte[] compute(byte[] data, int outputLength) throws GeneralSecurityException { 109 if (outputLength > maxOutputLength) { 110 throw new InvalidAlgorithmParameterException("tag size too big"); 111 } 112 113 localMac.get().update(data); 114 return Arrays.copyOf(localMac.get().doFinal(), outputLength); 115 } 116 117 /** Returns the maximum supported tag length. */ getMaxOutputLength()118 public int getMaxOutputLength() { 119 return maxOutputLength; 120 } 121 } 122