//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the Apache License Version 2.0.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
package com.microsoft.uprove;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import com.microsoft.uprove.Hashable;
import com.microsoft.uprove.HashFunction;
import com.microsoft.uprove.FieldZq.ZqElement;
/*
* LOW-LEVEL IMPLEMENTATION CLASS. NOT PART OF PUBLIC API.
*/
/**
* Private implementation of a <code>HashFunction</code>.
*/
final class HashFunctionImpl implements HashFunction {
private final MessageDigest md;
private final FieldZq Zq;
private final MDOutputStream mdSink;
private final HashFormatter formatter;
/**
* Constructs a new instance of this implementation.
* @param md the underlying <code>MessageDigest</code>.
* @param Zq the field into which resulting elements may be created.
*/
private HashFunctionImpl(final MessageDigest md, final FieldZq Zq) {
if (Zq == null) {
throw new NullPointerException();
}
this.md = md;
this.Zq = Zq;
md.reset();
this.mdSink = new MDOutputStream(md);
try {
this.formatter = new HashFormatter(mdSink);
} catch (IOException e) {
throw wrapIOException(e); // impossible
}
}
/**
* Copy constructor.
* @param hfi
* @throws CloneNotSupportedException
*/
private HashFunctionImpl(final HashFunctionImpl hfi)
throws CloneNotSupportedException {
this.md = (MessageDigest) hfi.md.clone();
this.Zq = hfi.Zq;
this.mdSink = new MDOutputStream(this.md);
this.formatter = new HashFormatter(hfi.formatter, mdSink);
}
/**
* Wraps an IOException in an AssertionError.
* @param e an IOException.
* @return an AssertionError holding <code>e</code>.
*/
private static AssertionError wrapIOException(final IOException e) {
AssertionError ae = new AssertionError("Impossible exception");
ae.initCause(e);
return ae;
}
private void doReset() {
try {
formatter.reset(mdSink);
} catch (IOException e) {
throw wrapIOException(e); // impossible
}
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashFunction#reset()
*/
public void reset() {
md.reset();
doReset();
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashFunction#getByteDigest()
*/
public byte[] getByteDigest() {
byte[] retVal = md.digest();
doReset();
return retVal;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashFunction#getDigestSize()
*/
public int getDigestSize() {
return md.getDigestLength();
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashFunction#getZqDigest()
*/
public ZqElement getZqDigest() {
// Zq constructor will do the mod q
ZqElement retVal = Zq.determineElement(new BigInteger(1, md.digest()));
doReset();
return retVal;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashFunction#getFieldZq()
*/
public FieldZq getFieldZq() {
return Zq;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashUpdater#update(byte)
*/
public void update(final byte b) {
try {
formatter.encode(b);
} catch (IOException e) {
throw wrapIOException(e); // impossible
}
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashUpdater#update(int)
*/
public void update(final int i) {
try {
formatter.encode(i);
} catch (IOException e) {
throw wrapIOException(e); // impossible
}
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashUpdater#update(byte[])
*/
public void update(final byte[] opaque) {
try {
formatter.encode(opaque);
} catch (IOException e) {
throw wrapIOException(e); // impossible
}
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashUpdater#update(byte[], int, int)
*/
public void update(final byte[] opaque, final int offset, final int len) {
try {
formatter.encode(opaque, offset, len);
} catch (IOException e) {
throw wrapIOException(e); // impossible
}
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashUpdater#update(BigInteger)
*/
public void update(final BigInteger i) {
if (i.signum() < 0) {
throw new IllegalArgumentException("i must be positive or zero");
}
byte[] bytes = i.toByteArray();
int index = (bytes[0] == (byte)0) ? 1 : 0;
update(bytes, index, bytes.length - index); // todo: optimize length computation!!!
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.HashUpdater#update(com.microsoft.uprove.crypto.Hashable)
*/
public void update(final Hashable d) {
d.addToDigest(this);
}
/*
* (non-Javadoc)
* @see com.microsoft.uprove.HashUpdater#updateNull()
*/
public void updateNull() {
update(0);
}
/**
* Creates a new hasher.
* @param algorithm the underlying <code>MessageDigest</code> algorithm to
* use.
* @param provider the provider to use, or <code>null</code> to use the
* default.
* @param Zq the field in which <code>Zq</code> digests are to be made.
* @return a new hash function instance.
* @throws NoSuchAlgorithmException if no providers implement
* <code>algorithm</code>.
* @throws NoSuchProviderException if <code>provider</code> is not
* registered.
*/
public static HashFunction getInstance(final String algorithm,
final String provider, final FieldZq Zq)
throws NoSuchAlgorithmException, NoSuchProviderException {
final MessageDigest md;
if (provider == null || provider.length() == 0) {
// look in config to find default provider
md = ConfigImpl.getMessageDigest(algorithm);
} else {
// we specify a provider
md = MessageDigest.getInstance(algorithm, provider);
}
return new HashFunctionImpl(md, Zq);
}
/**
* Creates a new hasher.
* @param algorithm the underlying <code>MessageDigest</code> algorithm to
* use.
* @param Zq the field in which <code>Zq</code> digests are to be made.
* @return a new hash function instance.
* @throws NoSuchAlgorithmException if no providers implement
* <code>algorithm</code>.
* @throws NoSuchProviderException if <code>provider</code> is not
* registered.
*/
public static HashFunction getInstance(final String algorithm,
final FieldZq Zq) throws NoSuchAlgorithmException,
NoSuchProviderException {
return getInstance(algorithm, null, Zq);
}
/* (non-Javadoc)
* @see java.lang.Object#clone()
*/
public Object clone() throws CloneNotSupportedException {
// we can't call super.clone() due to final fields
return new HashFunctionImpl(this);
}
}