| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.harmony.luni.util; |
| |
| |
| import java.io.OutputStream; |
| |
| /** |
| * This class implements the Secure Hash Algorithm, SHA-1. The specification can |
| * be found at http://csrc.ncsl.nist.gov/fips/fip180-1.txt |
| */ |
| public class SHAOutputStream extends OutputStream implements Cloneable { |
| |
| /* Constants as in the specification */ |
| |
| // K in iterations 0..19, from spec |
| private static final int K0_19 = 0x5a827999; |
| |
| // K in iterations 20..39, from spec |
| private static final int K20_39 = 0x6ed9eba1; |
| |
| // K in iterations 40..59, from spec |
| private static final int K40_59 = 0x8f1bbcdc; |
| |
| // K in iterations 60..79, from spec |
| private static final int K60_79 = 0xca62c1d6; |
| |
| // H0, from spec |
| private static final int H0 = 0x67452301; |
| |
| // H1, from spec |
| private static final int H1 = 0xefcdab89; |
| |
| // H2, from spec |
| private static final int H2 = 0x98badcfe; |
| |
| // H3, from spec |
| private static final int H3 = 0x10325476; |
| |
| // H4, from spec |
| private static final int H4 = 0xc3d2e1f0; |
| |
| private static final int HConstantsSize = 5; |
| |
| private static final int HashSizeInBytes = 20; |
| |
| // 16 words |
| private static final int BlockSizeInBytes = 16 * 4; |
| |
| // 80 words |
| private static final int WArraySize = 80; |
| |
| // 5-word Array. Starts with well-known constants, ends with SHA |
| private int[] HConstants; |
| |
| // 80-word Array. |
| private int[] WArray; |
| |
| // 16-word Array. Input bit stream M is divided in chunks of MArray |
| private byte[] MArray; |
| |
| // Number of bytes of input already processed towards SHA result |
| private long bytesProcessed; |
| |
| // Number of bytes in WArray not processed yet |
| private int bytesToProcess; |
| |
| // Optimization, for write |
| private byte[] oneByte = new byte[1]; |
| |
| /** |
| * Constructs a new SHAOutputStream. |
| */ |
| public SHAOutputStream() { |
| super(); |
| initialize(); |
| reset(); |
| } |
| |
| /** |
| * Constructs a new MD5OutputStream with the given initial state. |
| * |
| * @param state The initial state of the output stream. This is what will be |
| * returned by getHash() if write() is never called. |
| * |
| * @throws IllegalArgumentException if state.length is less than 16. |
| */ |
| public SHAOutputStream(byte[] state) { |
| this(); |
| |
| if (state.length < HashSizeInBytes) { |
| throw new IllegalArgumentException(); |
| } |
| |
| for (int i = 0; i < 4; i++) { |
| HConstants[i] = 0; |
| for (int j = 0; j < 4; j++) { |
| HConstants[i] += (state[4 * i + j] & 0xFF) << 8 * (3 - j); |
| } |
| } |
| } |
| |
| /** |
| * Answers a new instance of the same class as the receiver, whose slots |
| * have been filled in with the values in the slots of the receiver. |
| * <p> |
| * Classes which wish to support cloning must specify that they implement |
| * the Cloneable interface, since the native implementation checks for this. |
| * |
| * @return a complete copy of this object |
| * @throws CloneNotSupportedException if the component does not implement |
| * the interface Cloneable |
| */ |
| @Override |
| public Object clone() throws CloneNotSupportedException { |
| // Calling super takes care of primitive type slots |
| SHAOutputStream result = (SHAOutputStream) super.clone(); |
| result.HConstants = this.HConstants.clone(); |
| result.WArray = this.WArray.clone(); |
| result.MArray = this.MArray.clone(); |
| result.oneByte = this.oneByte.clone(); |
| return result; |
| } |
| |
| /** |
| * Copies a byte array into the receiver's internal buffer, making the |
| * adjustments as necessary and keeping the receiver in a consistent state. |
| * |
| * @param buffer byte array representation of the bytes |
| * @param off offset into the source buffer where to start the copying |
| * @param len how many bytes in the source byte array to copy |
| * |
| */ |
| private void copyToInternalBuffer(byte[] buffer, int off, int len) { |
| int index; |
| index = off; |
| for (int i = bytesToProcess; i < bytesToProcess + len; i++) { |
| MArray[i] = buffer[index]; |
| index++; |
| } |
| bytesToProcess = bytesToProcess + len; |
| } |
| |
| /** |
| * Returns an int array (length = 5) with the SHA value of the bytes written |
| * to the receiver. |
| * |
| * @return The 5 ints that form the SHA value of the bytes written to |
| * the receiver |
| */ |
| public int[] getHash() { |
| this.padBuffer(); |
| this.processBuffer(); |
| int[] result = HConstants.clone(); |
| // After the user asks for the hash value, the stream is put back to the |
| // initial state |
| reset(); |
| return result; |
| } |
| |
| /** |
| * Returns a byte array (length = 20) with the SHA value of the bytes |
| * written to the receiver. |
| * |
| * @return The bytes that form the SHA value of the bytes written to |
| * the receiver |
| */ |
| public byte[] getHashAsBytes() { |
| byte[] hash = new byte[HashSizeInBytes]; |
| this.padBuffer(); |
| this.processBuffer(); |
| |
| // We need to return HConstants (modified by the loop) as an array of |
| // bytes. A memcopy would be the fastest. |
| for (int i = 0; i < (HashSizeInBytes / 4); ++i) { |
| hash[i * 4] = (byte) (HConstants[i] >>> 24 & 0xff); |
| hash[i * 4 + 1] = (byte) (HConstants[i] >>> 16 & 0xff); |
| hash[i * 4 + 2] = (byte) (HConstants[i] >>> 8 & 0xff); |
| hash[i * 4 + 3] = (byte) (HConstants[i] & 0xff); |
| } |
| // After the user asks for the hash value, the stream is put back to the |
| // initial state |
| reset(); |
| return hash; |
| } |
| |
| /** |
| * Returns a byte array (length = 20) with the SHA value of the bytes |
| * written to the receiver. |
| * |
| * @return The bytes that form the SHA value of the bytes written to |
| * the receiver |
| */ |
| public byte[] getHashAsBytes(boolean pad) { |
| byte[] hash = new byte[HashSizeInBytes]; |
| if (pad) { |
| this.padBuffer(); |
| this.processBuffer(); |
| } |
| |
| // Convert HConstants to an array of bytes |
| for (int i = 0; i < (HashSizeInBytes / 4); i++) { |
| hash[i * 4] = (byte) (HConstants[i] >>> 24 & 0xff); |
| hash[i * 4 + 1] = (byte) (HConstants[i] >>> 16 & 0xff); |
| hash[i * 4 + 2] = (byte) (HConstants[i] >>> 8 & 0xff); |
| hash[i * 4 + 3] = (byte) (HConstants[i] & 0xff); |
| } |
| // After the user asks for the hash value, the stream is put back to the |
| // initial state |
| reset(); |
| return hash; |
| } |
| |
| /** |
| * Initializes the receiver. |
| */ |
| private void initialize() { |
| HConstants = new int[HConstantsSize]; |
| MArray = new byte[BlockSizeInBytes]; |
| WArray = new int[WArraySize]; |
| } |
| |
| /** |
| * Adds extra bytes to the bit stream as required (see the SHA |
| * specification). |
| */ |
| private void padBuffer() { |
| long lengthInBits; |
| MArray[bytesToProcess] = (byte) 0x80; |
| for (int i = bytesToProcess + 1; i < BlockSizeInBytes; i++) { |
| MArray[i] = (byte) 0; |
| } |
| // Get length now because there might be extra padding (length in bits) |
| lengthInBits = (bytesToProcess + bytesProcessed) * 8; |
| |
| // 9 extra bytes are needed: 1 for 1000... and 8 for length (long) |
| if ((bytesToProcess + 9) > BlockSizeInBytes) { |
| // Not enough space to append length. We need another block for |
| // padding |
| // Padding in this buffer only includes 1000000.... |
| this.processBuffer(); |
| // Now put 0's in all the buffer. memfill would be faster |
| for (int i = 0; i < BlockSizeInBytes; i++) { |
| MArray[i] = (byte) 0; |
| } |
| } |
| |
| for (int i = 1; i < 9; i++) { |
| MArray[BlockSizeInBytes - i] = (byte) (lengthInBits & 0xff); |
| lengthInBits = lengthInBits >>> 8; |
| } |
| } |
| |
| /** |
| * Core SHA code. Processes the receiver's buffer of bits, computing the |
| * values towards the final SHA |
| */ |
| private void processBuffer() { |
| int A; // A variable, from spec |
| int B; // B variable, from spec |
| int C; // C variable, from spec |
| int D; // D variable, from spec |
| int E; // E variable, from spec |
| int temp; // TEMP, from spec |
| int t; // t, for iteration, from spec |
| |
| for (t = 0; t <= 15; t++) { // step a, page 7 of spec. Here we convert 4 |
| // bytes into 1 word, 16 times |
| WArray[t] = (MArray[4 * t] & 0xff) << 24 |
| | ((MArray[4 * t + 1] & 0xff) << 16) |
| | ((MArray[4 * t + 2] & 0xff) << 8) |
| | (MArray[4 * t + 3] & 0xff); |
| } |
| for (t = 16; t <= 79; t++) { // step b, page 7 of spec |
| temp = (WArray[t - 3] ^ WArray[t - 8] ^ WArray[t - 14] ^ WArray[t - 16]); |
| temp = (temp << 1) | (temp >>> (32 - 1)); // element , Circular |
| // Shift Left by 1 |
| WArray[t] = temp; |
| } |
| |
| // step c, page 7 of spec |
| A = HConstants[0]; |
| B = HConstants[1]; |
| C = HConstants[2]; |
| D = HConstants[3]; |
| E = HConstants[4]; |
| |
| // step d, page 8 of spec |
| for (t = 0; t <= 19; t++) { |
| temp = (A << 5) | (A >>> (32 - 5)); // A , Circular Shift Left by 5 |
| temp = temp + E + WArray[t] + K0_19; |
| temp = temp + ((B & C) | (~B & D)); |
| E = D; |
| D = C; |
| C = (B << 30) | (B >>> (32 - 30)); // B , Circular Shift Left by 30 |
| B = A; |
| A = temp; |
| } |
| for (t = 20; t <= 39; t++) { |
| temp = (A << 5) | (A >>> (32 - 5)); // A , Circular Shift Left by 5 |
| temp = temp + E + WArray[t] + K20_39; |
| temp = temp + (B ^ C ^ D); |
| E = D; |
| D = C; |
| C = (B << 30) | (B >>> (32 - 30)); // B , Circular Shift Left by 30 |
| B = A; |
| A = temp; |
| } |
| for (t = 40; t <= 59; t++) { |
| temp = (A << 5) | (A >>> (32 - 5)); // A , Circular Shift Left by 5 |
| temp = temp + E + WArray[t] + K40_59; |
| temp = temp + ((B & C) | (B & D) | (C & D)); |
| E = D; |
| D = C; |
| C = (B << 30) | (B >>> (32 - 30)); // B , Circular Shift Left by 30 |
| B = A; |
| A = temp; |
| } |
| for (t = 60; t <= 79; t++) { |
| temp = (A << 5) | (A >>> (32 - 5)); // A , Circular Shift Left by 5 |
| temp = temp + E + WArray[t] + K60_79; |
| temp = temp + (B ^ C ^ D); |
| E = D; |
| D = C; |
| C = (B << 30) | (B >>> (32 - 30)); // B , Circular Shift Left by 30 |
| B = A; |
| A = temp; |
| } |
| |
| // step e, page 8 of spec |
| HConstants[0] = HConstants[0] + A; |
| HConstants[1] = HConstants[1] + B; |
| HConstants[2] = HConstants[2] + C; |
| HConstants[3] = HConstants[3] + D; |
| HConstants[4] = HConstants[4] + E; |
| // Update number of bytes actually processed |
| bytesProcessed = bytesProcessed + BlockSizeInBytes; |
| bytesToProcess = 0; // No pending bytes in the block |
| |
| } |
| |
| /** |
| * Reset this SHAOutputStream to the state it was before any byte was |
| * written to it. |
| */ |
| public void reset() { |
| HConstants[0] = H0; |
| HConstants[1] = H1; |
| HConstants[2] = H2; |
| HConstants[3] = H3; |
| HConstants[4] = H4; |
| bytesProcessed = 0; |
| bytesToProcess = 0; |
| } |
| |
| @Override |
| public String toString() { |
| return this.getClass().getName() + ':' + toStringBlock(getHashAsBytes()); |
| } |
| |
| /** |
| * Converts a block to a String representation. |
| * |
| * @param block |
| * byte array representation of the bytes |
| */ |
| private static String toStringBlock(byte[] block) { |
| return toStringBlock(block, 0, block.length); |
| } |
| |
| /** |
| * Converts a block to a String representation. |
| * |
| * @param block |
| * byte array representation of the bytes |
| * @param off |
| * offset into the block where to start the conversion |
| * @param len |
| * how many bytes in the byte array to convert to a printable |
| * representation |
| */ |
| private static String toStringBlock(byte[] block, int off, int len) { |
| String hexdigits = "0123456789ABCDEF"; |
| StringBuilder buf = new StringBuilder(); |
| buf.append('['); |
| for (int i = off; i < off + len; ++i) { |
| buf.append(hexdigits.charAt((block[i] >>> 4) & 0xf)); |
| buf.append(hexdigits.charAt(block[i] & 0xf)); |
| } |
| buf.append(']'); |
| return buf.toString(); |
| } |
| |
| /** |
| * Writes <code>len</code> <code>bytes</code> from this byte array |
| * <code>buffer</code> starting at offset <code>off</code> to the |
| * SHAOutputStream. The internal buffer used to compute SHA is updated, and |
| * the incremental computation of SHA is also performed. |
| * |
| * @param buffer |
| * the buffer to be written |
| * @param off |
| * offset in buffer to get bytes |
| * @param len |
| * number of bytes in buffer to write |
| */ |
| @Override |
| public void write(byte[] buffer, int off, int len) { |
| int spaceLeft; |
| int start; |
| int bytesLeft; |
| spaceLeft = BlockSizeInBytes - bytesToProcess; |
| if (len < spaceLeft) { // Extra bytes are not enough to fill buffer |
| this.copyToInternalBuffer(buffer, off, len); |
| return; |
| } |
| // Extra bytes are bigger than space in buffer. Process buffer multiple |
| // times |
| this.copyToInternalBuffer(buffer, off, spaceLeft); |
| bytesLeft = len - spaceLeft; |
| this.processBuffer(); |
| start = off + spaceLeft; |
| while (bytesLeft >= BlockSizeInBytes) { |
| this.copyToInternalBuffer(buffer, start, BlockSizeInBytes); |
| bytesLeft = bytesLeft - BlockSizeInBytes; |
| this.processBuffer(); |
| start = start + BlockSizeInBytes; |
| } |
| if (bytesLeft > 0) { |
| // Extra bytes at the end, not enough to fill buffer |
| this.copyToInternalBuffer(buffer, start, bytesLeft); |
| } |
| } |
| |
| /** |
| * Writes the specified byte <code>b</code> to this OutputStream. Only the |
| * low order byte of <code>b</code> is written. |
| * |
| * @param b |
| * the byte to be written |
| */ |
| @Override |
| public void write(int b) { |
| // Not thread-safe because we use a shared one-byte buffer |
| oneByte[0] = (byte) b; |
| write(oneByte, 0, 1); |
| } |
| } |