• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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