/** * Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.redPandaLib.crypt; import com.google.common.io.ByteStreams; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.Serializable; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import org.spongycastle.util.encoders.Hex; /** * A Sha256Hash just wraps a byte[] so that equals and hashcode work correctly, * allowing it to be used as keys in a map. It also checks that the length is * correct and provides a bit more type safety. */ public class Sha256Hash implements Serializable { private byte[] bytes; public static final Sha256Hash ZERO_HASH = new Sha256Hash(new byte[32]); /** * Creates a Sha256Hash by wrapping the given byte array. It must be 32 * bytes long. */ public Sha256Hash(byte[] rawHashBytes) { checkArgument(rawHashBytes.length == 32); this.bytes = rawHashBytes; } /** * Creates a Sha256Hash by decoding the given hex string. It must be 64 * characters long. */ public Sha256Hash(String hexString) { checkArgument(hexString.length() == 64); this.bytes = Hex.decode(hexString); } /** * Calculates the (one-time) hash of contents and returns it as a new * wrapped hash. */ public static Sha256Hash create(byte[] contents) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); return new Sha256Hash(digest.digest(contents)); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); // Cannot happen. } } /** * Calculates the hash of the hash of the contents. This is a standard * operation in Bitcoin. */ public static Sha256Hash createDouble(byte[] contents) { return new Sha256Hash(Utils.doubleDigest(contents)); } /** * Returns a hash of the given files contents. Reads the file fully into * memory before hashing so only use with small files. * * @throws IOException */ public static Sha256Hash hashFileContents(File f) throws IOException { FileInputStream in = new FileInputStream(f); try { return create(ByteStreams.toByteArray(in)); } finally { in.close(); } } /** * Returns true if the hashes are equal. */ @Override public boolean equals(Object other) { if (!(other instanceof Sha256Hash)) { return false; } return Arrays.equals(bytes, ((Sha256Hash) other).bytes); } /** * Hash code of the byte array as calculated by {@link Arrays#hashCode()}. * Note the difference between a SHA256 secure bytes and the type of * quick/dirty bytes used by the Java hashCode method which is designed for * use in bytes tables. */ @Override public int hashCode() { // Use the last 4 bytes, not the first 4 which are often zeros in Bitcoin. return (bytes[31] & 0xFF) | ((bytes[30] & 0xFF) << 8) | ((bytes[29] & 0xFF) << 16) | ((bytes[28] & 0xFF) << 24); } @Override public String toString() { return Utils.bytesToHexString(bytes); } /** * Returns the bytes interpreted as a positive integer. */ public BigInteger toBigInteger() { return new BigInteger(1, bytes); } public byte[] getBytes() { return bytes; } public Sha256Hash duplicate() { return new Sha256Hash(bytes); } private static void checkArgument(boolean b) { if (!b) { throw new IllegalArgumentException(); } } }