/*
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This is a combination and adaptation of subsets of
* java.security.MessageDigest and
* sun.security.provider.DigestBase to provide a class offering
* most of the same public methods of MessageDigest while not
* depending on the Java Security Framework.
*
* One algorithm is currently supported: "SHA-1". *
* NOTE If java.security.MessageDigest,
* sun.security.provider.DigestBase or
* sun.security.provider.SHA are modified, review of those
* modifications should be done to determine any possible implications for this
* class and StandaloneSHA.
*/
package sun.jkernel;
import java.security.DigestException;
import java.security.ProviderException;
import java.security.NoSuchAlgorithmException;
/**
* (Adapted from the sun.security.provider.DigestBase doc).
* This is a simple subset of the Common base message digest implementation
* for the Sun provider.
* It implements most of the JCA methods as suitable for a Java message
* digest
* implementation of an algorithm based on a compression function (as all
* commonly used algorithms are). The individual digest subclasses only need to
* implement the following methods:
*
* . abstract void implCompress(byte[] b, int ofs);
* . abstract void implDigest(byte[] out, int ofs);
* . abstract void implReset();
*
* No support for a clone() method is provided. *
* See the inline documentation for details.
*
* @since 1.5
* @version 1.3, 08/08/07
* @author Andreas Sterbenz (MessageDigest)
* @author Pete Soper (this derived class)
*/
public abstract class StandaloneMessageDigest {
public static final boolean debug = false;
/*
* (Copied/adapted from java.security.MessageDigest
*
* This is a subset/simplification java.security.MessageDigest
* that supports a fixed set of hashcode mechanisms (currently just
* SHA-1) while preserving the following MessageDigest methods:
*
* public MessageDigest getInstance(String algorithm)
* public final int getDigestLength()
* public void reset()
* public byte[] digest()
* public void update(byte[] input, int offset, int len)
* public final String getAlgorithm()
*
* NOTE that the clone() method is not provided. */ /** * Prevent direct instantiation except via the factory method. */ private StandaloneMessageDigest() { // Keep javac happy. digestLength = 0; blockSize = 0; algorithm = null; buffer = null; } private String algorithm; // The state of this digest private static final int INITIAL = 0; private static final int IN_PROGRESS = 1; private int state = INITIAL; /** * Returns a StandaloneMessageDigest object that implements the specified * digest algorithm. * *
This method returns a new StandaloneMessageDigest for a single
* algorithm provider.
*
* @param algorithm the name of the algorithm requested.
*
* @return a standalone Message Digest object that implements the specified algorithm.
*
* @exception NoSuchAlgorithmException if algorithm not supported
*
*/
public static StandaloneMessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (! algorithm.equals("SHA-1")) {
throw new NoSuchAlgorithmException(algorithm + " not found");
} else {
return new StandaloneSHA();
}
}
/**
* Updates the digest using the specified array of bytes, starting
* at the specified offset.
*
* @param input the array of bytes.
*
* @param offset the offset to start from in the array of bytes.
*
* @param len the number of bytes to use, starting at
* offset.
*/
public void update(byte[] input, int offset, int len) {
if (debug) {
System.out.println("StandaloneMessageDigest.update");
(new Exception()).printStackTrace();
}
if (input == null) {
throw new IllegalArgumentException("No input buffer given");
}
if (input.length - offset < len) {
throw new IllegalArgumentException("Input buffer too short");
}
// No need to check for negative offset: engineUpdate does this
engineUpdate(input, offset, len);
state = IN_PROGRESS;
}
/**
* Completes the hash computation by performing final operations
* such as padding. The digest is reset after this call is made.
*
* @return the array of bytes for the resulting hash value.
*/
public byte[] digest() {
if (debug) {
System.out.println("StandaloneMessageDigest.digest");
}
/* Resetting is the responsibility of implementors. */
byte[] result = engineDigest();
state = INITIAL;
return result;
}
/**
* Compares two digests for equality. Does a simple byte compare.
*
* @param digesta one of the digests to compare.
*
* @param digestb the other digest to compare.
*
* @return true if the digests are equal, false otherwise.
*/
public static boolean isEqual(byte digesta[], byte digestb[]) {
if (digesta.length != digestb.length)
return false;
for (int i = 0; i < digesta.length; i++) {
if (digesta[i] != digestb[i]) {
return false;
}
}
return true;
}
/**
* Resets the digest for further use.
*/
public void reset() {
if (debug) {
System.out.println("StandaloneMessageDigest.reset");
}
engineReset();
state = INITIAL;
}
/**
* Returns a string that identifies the algorithm, independent of
* implementation details. The name should be a standard
* Java Security name (such as "SHA", "MD5", and so on).
* See Appendix A in the
* Java Cryptography Architecture API Specification & Reference
* for information about standard algorithm names.
*
* @return the name of the algorithm
*/
public final String getAlgorithm() {
return this.algorithm;
}
/**
* Returns the length of the digest in bytes.
*
* @return the digest length in bytes.
*
* @since 1.2
*/
public final int getDigestLength() {
return engineGetDigestLength();
}
//* End of copied/adapted java.security.MessageDigest
// Start of copied/adapted sun.security.provider.DigestBase
// one element byte array, temporary storage for update(byte)
private byte[] oneByte;
// length of the message digest in bytes
private final int digestLength;
// size of the input to the compression function in bytes
private final int blockSize;
// buffer to store partial blocks, blockSize bytes large
// Subclasses should not access this array directly except possibly in their
// implDigest() method. See MD5.java as an example.
final byte[] buffer;
// offset into buffer
private int bufOfs;
// number of bytes processed so far. subclasses should not modify
// this value.
// also used as a flag to indicate reset status
// -1: need to call engineReset() before next call to update()
// 0: is already reset
long bytesProcessed;
/**
* Main constructor.
*/
StandaloneMessageDigest(String algorithm, int digestLength, int blockSize) {
// super();
this.algorithm = algorithm;
this.digestLength = digestLength;
this.blockSize = blockSize;
buffer = new byte[blockSize];
}
// return digest length. See JCA doc.
protected final int engineGetDigestLength() {
return digestLength;
}
// single byte update. See JCA doc.
protected final void engineUpdate(byte b) {
if (oneByte == null) {
oneByte = new byte[1];
}
oneByte[0] = b;
engineUpdate(oneByte, 0, 1);
}
// array update. See JCA doc.
protected final void engineUpdate(byte[] b, int ofs, int len) {
if (len == 0) {
return;
}
if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) {
throw new ArrayIndexOutOfBoundsException();
}
if (bytesProcessed < 0) {
engineReset();
}
bytesProcessed += len;
// if buffer is not empty, we need to fill it before proceeding
if (bufOfs != 0) {
int n = Math.min(len, blockSize - bufOfs);
System.arraycopy(b, ofs, buffer, bufOfs, n);
bufOfs += n;
ofs += n;
len -= n;
if (bufOfs >= blockSize) {
// compress completed block now
implCompress(buffer, 0);
bufOfs = 0;
}
}
// compress complete blocks
while (len >= blockSize) {
implCompress(b, ofs);
len -= blockSize;
ofs += blockSize;
}
// copy remainder to buffer
if (len > 0) {
System.arraycopy(b, ofs, buffer, 0, len);
bufOfs = len;
}
}
// reset this object. See JCA doc.
protected final void engineReset() {
if (bytesProcessed == 0) {
// already reset, ignore
return;
}
implReset();
bufOfs = 0;
bytesProcessed = 0;
}
// return the digest. See JCA doc.
protected final byte[] engineDigest() throws ProviderException {
byte[] b = new byte[digestLength];
try {
engineDigest(b, 0, b.length);
} catch (DigestException e) {
throw (ProviderException)
new ProviderException("Internal error").initCause(e);
}
return b;
}
// return the digest in the specified array. See JCA doc.
protected final int engineDigest(byte[] out, int ofs, int len)
throws DigestException {
if (len < digestLength) {
throw new DigestException("Length must be at least "
+ digestLength + " for " + algorithm + "digests");
}
if ((ofs < 0) || (len < 0) || (ofs > out.length - len)) {
throw new DigestException("Buffer too short to store digest");
}
if (bytesProcessed < 0) {
engineReset();
}
implDigest(out, ofs);
bytesProcessed = -1;
return digestLength;
}
/**
* Core compression function. Processes blockSize bytes at a time
* and updates the state of this object.
*/
abstract void implCompress(byte[] b, int ofs);
/**
* Return the digest. Subclasses do not need to reset() themselves,
* StandaloneMessageDigest calls implReset() when necessary.
*/
abstract void implDigest(byte[] out, int ofs);
/**
* Reset subclass specific state to their initial values. StandaloneMessageDigest
* calls this method when necessary.
*/
abstract void implReset();
// padding used for the MD5, and SHA-* message digests
static final byte[] padding;
static {
// we need 128 byte padding for SHA-384/512
// and an additional 8 bytes for the high 8 bytes of the 16
// byte bit counter in SHA-384/512
padding = new byte[136];
padding[0] = (byte)0x80;
}
}