• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. 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 distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.hash;
16 
17 import static com.google.common.base.Preconditions.checkNotNull;
18 import static com.google.common.base.Preconditions.checkState;
19 
20 import com.google.errorprone.annotations.Immutable;
21 import java.nio.ByteBuffer;
22 import java.security.InvalidKeyException;
23 import java.security.Key;
24 import java.security.NoSuchAlgorithmException;
25 import javax.crypto.Mac;
26 
27 /**
28  * {@link HashFunction} adapter for {@link Mac} instances.
29  *
30  * @author Kurt Alfred Kluever
31  */
32 @Immutable
33 @ElementTypesAreNonnullByDefault
34 final class MacHashFunction extends AbstractHashFunction {
35 
36   @SuppressWarnings("Immutable") // cloned before each use
37   private final Mac prototype;
38 
39   @SuppressWarnings("Immutable") // keys are immutable, but not provably so
40   private final Key key;
41 
42   private final String toString;
43   private final int bits;
44   private final boolean supportsClone;
45 
MacHashFunction(String algorithmName, Key key, String toString)46   MacHashFunction(String algorithmName, Key key, String toString) {
47     this.prototype = getMac(algorithmName, key);
48     this.key = checkNotNull(key);
49     this.toString = checkNotNull(toString);
50     this.bits = prototype.getMacLength() * Byte.SIZE;
51     this.supportsClone = supportsClone(prototype);
52   }
53 
54   @Override
bits()55   public int bits() {
56     return bits;
57   }
58 
supportsClone(Mac mac)59   private static boolean supportsClone(Mac mac) {
60     try {
61       Object unused = mac.clone();
62       return true;
63     } catch (CloneNotSupportedException e) {
64       return false;
65     }
66   }
67 
getMac(String algorithmName, Key key)68   private static Mac getMac(String algorithmName, Key key) {
69     try {
70       Mac mac = Mac.getInstance(algorithmName);
71       mac.init(key);
72       return mac;
73     } catch (NoSuchAlgorithmException e) {
74       throw new IllegalStateException(e);
75     } catch (InvalidKeyException e) {
76       throw new IllegalArgumentException(e);
77     }
78   }
79 
80   @Override
newHasher()81   public Hasher newHasher() {
82     if (supportsClone) {
83       try {
84         return new MacHasher((Mac) prototype.clone());
85       } catch (CloneNotSupportedException e) {
86         // falls through
87       }
88     }
89     return new MacHasher(getMac(prototype.getAlgorithm(), key));
90   }
91 
92   @Override
toString()93   public String toString() {
94     return toString;
95   }
96 
97   /** Hasher that updates a {@link Mac} (message authentication code). */
98   private static final class MacHasher extends AbstractByteHasher {
99     private final Mac mac;
100     private boolean done;
101 
MacHasher(Mac mac)102     private MacHasher(Mac mac) {
103       this.mac = mac;
104     }
105 
106     @Override
update(byte b)107     protected void update(byte b) {
108       checkNotDone();
109       mac.update(b);
110     }
111 
112     @Override
update(byte[] b)113     protected void update(byte[] b) {
114       checkNotDone();
115       mac.update(b);
116     }
117 
118     @Override
update(byte[] b, int off, int len)119     protected void update(byte[] b, int off, int len) {
120       checkNotDone();
121       mac.update(b, off, len);
122     }
123 
124     @Override
update(ByteBuffer bytes)125     protected void update(ByteBuffer bytes) {
126       checkNotDone();
127       checkNotNull(bytes);
128       mac.update(bytes);
129     }
130 
checkNotDone()131     private void checkNotDone() {
132       checkState(!done, "Cannot re-use a Hasher after calling hash() on it");
133     }
134 
135     @Override
hash()136     public HashCode hash() {
137       checkNotDone();
138       done = true;
139       return HashCode.fromBytesNoCopy(mac.doFinal());
140     }
141   }
142 }
143