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