//*********************************************************
//
// 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.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
/**
* Internal representation of issuer parameters.
*/
class IssuerParametersInternal {
// the parameters UID
private byte[] parametersUID;
// the prime order group
private PrimeOrderGroup group;
// the hash algorithm UID
private String hashAlgorithmUID;
// the public key elements
private GroupElement[] publicKey;
// the hash booleans
private byte[] encodingBytes;
// prover's issuance values
private GroupElement[] proverIssuanceValues;
// the specification
private byte[] specification;
// private protocol data
private byte[] issuerParametersDigest;
private HashFunction hashPrototype;
private boolean triedCloningPrototype = false;
public IssuerParametersInternal() {
super();
}
/**
* Indicates whether some other object is "equal to" this one.
* @param obj the reference object with which to compare.
* @return <code>true</code> if this object is the same as the
* <code>obj</code> argument; <code>false</code> otherwise.
*/
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof IssuerParametersInternal)) {
return false;
}
IssuerParametersInternal ip = (IssuerParametersInternal) obj;
if (!Arrays.equals(this.parametersUID, ip.parametersUID)
|| !this.group.equals(ip.group)
|| !this.hashAlgorithmUID.equals(ip.hashAlgorithmUID)
|| !Arrays.equals(this.publicKey, ip.publicKey)
|| !Arrays.equals(this.encodingBytes, ip.encodingBytes)
|| !Arrays.equals(this.proverIssuanceValues, this.proverIssuanceValues)
|| !Arrays.equals(this.specification, ip.specification))
{
return false;
}
return true;
}
/**
* Returns a hash code value for the object.
* @return a hash code value for the object.
*/
public int hashCode() {
int result = 233;
result = 229 * result + Arrays.hashCode(this.parametersUID);
result = 229 * result + this.group.hashCode();
result = 229 * result + this.hashAlgorithmUID.hashCode();
result = 229 * result + Arrays.hashCode(this.publicKey);
result = 229 * result + Arrays.hashCode(this.encodingBytes);
result = 229 * result + Arrays.hashCode(this.proverIssuanceValues);
result = 229 * result + Arrays.hashCode(this.specification);
return result;
}
/**
* Returns a string representation of the Issuer parameters object.
* <p>This method returns a string equal to the value of:</p>
* <pre>
* getClass().getName() + '@' + Integer.toHexString(hashCode())
* + ':' + getUID()
* </pre>
* @return a string representation of the Issuer parameters object.
*/
public String toString() {
return new StringBuffer(super.toString()).append(':').append(ByteArrays.toString(parametersUID))
.toString();
}
/**
* Returns true if Device-protected is supported, false otherwise.
* @return if Device-protected is supported.
*/
public boolean supportsDevice() {
return publicKey.length == encodingBytes.length + 3;
}
/**
* @return the parametersUID
*/
public byte[] getParametersUID() {
return parametersUID;
}
/**
* @param parametersUID the parametersUID to set
*/
public void setParametersUID(byte[] parametersUID) {
this.parametersUID = parametersUID;
}
/**
* @return the group
*/
public PrimeOrderGroup getGroup() {
return group;
}
/**
* @param group the group to set
*/
public void setGroup(PrimeOrderGroup group) {
this.group = group;
}
/**
* @return the hashAlgorithmUID
*/
public String getHashAlgorithmUID() {
return hashAlgorithmUID;
}
/**
* @param hashAlgorithmUID the hashAlgorithmUID to set
*/
public void setHashAlgorithmUID(String hashAlgorithmUID) {
this.hashAlgorithmUID = hashAlgorithmUID;
}
/**
* @return the publicKey
*/
public GroupElement[] getPublicKey() {
return publicKey;
}
/**
* @param publicKey the publicKey to set
*/
public void setPublicKey(GroupElement[] publicKey) {
this.publicKey = publicKey;
}
public GroupElement getDeviceGenerator() {
if (!supportsDevice()) {
throw new IllegalStateException("Issuer parameters do not support Device");
}
return publicKey[publicKey.length-1]; // gd
}
/**
* @return the encodingBytes
*/
public byte[] getEncodingBytes() {
return encodingBytes;
}
/**
* @param encodingBytes the encodingBytes to set
*/
public void setEncodingBytes(byte[] encodingBytes) {
this.encodingBytes = encodingBytes;
}
/**
* @return the proverIssuanceValues
*/
public GroupElement[] getProverIssuanceValues() {
return proverIssuanceValues;
}
/**
* @param proverIssuanceValues the proverIssuanceValues to set
*/
public void setProverIssuanceValues(GroupElement[] proverIssuanceValues) {
this.proverIssuanceValues = proverIssuanceValues;
}
/**
* @return the specification
*/
public byte[] getSpecification() {
return specification;
}
/**
* @param specification the specification to set
*/
public void setSpecification(byte[] specification) {
this.specification = specification;
}
/**
* Validates the consistency of the Issuer Parameters's elements. This method
* should be called once for every externally received Issuer Parameters.
* @throws IllegalStateException if the Issuer Parameters's mathematical
* group is malformed or if the Issuer public key elements are not part of
* the group.
*/
void validate() throws IllegalStateException {
// validate the group
group.validate();
// make sure that all Issuer public key elements are member of Gq
int numElements = publicKey.length;
for (int i = 0; i < numElements; i++) {
// check 1 < g_i < p
if (group.getIdentity().equals(publicKey[i])) {
throw new IllegalStateException("Public key element equals 1");
}
// it is guaranteed that all pubKey elements will belong
// to the Gq instance, otherwise they won't be usable
if (!publicKey[i].isValid()) {
throw new IllegalStateException("Public key element " + i + " is not in group");
}
}
}
/**
* Instantiate a new <code>HashFunction</code> object using the
* hash algorithm specified in the parameters (obtainable by
* calling {@link #getHashAlgorithmUID()}.
* @return a <code>HashFunction</code> object.
* @throws IllegalStateException if hash function can't be initialized
*/
HashFunction getHashFunction() {
if (!triedCloningPrototype) {
// this is the first time this method is called, let's try to create a prototype
// see if we can use the chosen hashAlgorithm and if we can clone
// a prototype rather than creating a new one each time around.
HashFunction hf = null;
HashFunction clone = null;
try {
hf = HashFunctionImpl.getInstance(hashAlgorithmUID, group.getZq());
clone = (HashFunction) hf.clone();
} catch (CloneNotSupportedException cnse) {
// oh well, can't use a prototype
} catch (NoSuchAlgorithmException e) {
// defer notifying the user until they try to get an instance
} catch (NoSuchProviderException e) {
// defer notifying the user until they try to get an instance
}
// if we were able to clone a prototype, keep it around for later
hashPrototype = (clone == null ? null : hf);
triedCloningPrototype = true;
}
// clone the prototype if possible
if (hashPrototype != null) {
try {
return (HashFunction) hashPrototype.clone();
} catch (CloneNotSupportedException e) {
AssertionError ae = new AssertionError("Impossible exception");
ae.initCause(e);
throw ae;
}
}
// return a new instance
IllegalStateException ise = null;
try {
return HashFunctionImpl.getInstance(this.hashAlgorithmUID, group.getZq());
} catch (NoSuchAlgorithmException e) {
ise = new IllegalStateException(
"Unable to initialize hash function");
ise.initCause(e);
} catch (NoSuchProviderException e) {
ise = new IllegalStateException(
"Unable to initialize hash function");
ise.initCause(e);
}
throw ise;
}
byte[] getIssuerParametersDigest() {
if (this.issuerParametersDigest != null) {
return this.issuerParametersDigest;
}
// compute the issuer parameters digest
HashFunction H = getHashFunction();
// UIDp
H.update(this.parametersUID);
// p, q, g
H.update(group);
// TODO: support the case if IP supports device but token is not device-protected.
// <g0, g1, ..., gn, gt, [g_d]>
H.update(this.publicKey.length); // size
for (int i=0; i<(this.publicKey.length); i++) {
H.update(this.publicKey[i]);
}
// <e1, ..., en>
H.update(this.encodingBytes.length); // size
for (int i=0; i<this.encodingBytes.length; i++) {
H.update(this.encodingBytes[i]);
}
// S
H.update(this.specification);
this.issuerParametersDigest = H.getByteDigest();
return this.issuerParametersDigest;
}
/**
* Generates an internal representation of an issuer parameters.
* @param ip the issuer parameters.
* @return an internal representation of the issuer parameters.
* @throws IOException if the issuer parameters are malformed.
*/
static IssuerParametersInternal generate(IssuerParameters ip) throws IOException {
IssuerParametersInternal ipi = new IssuerParametersInternal();
ipi.setParametersUID(ip.getParametersUID());
ipi.setEncodingBytes(ip.getEncodingBytes());
ipi.setHashAlgorithmUID(ip.getHashAlgorithmUID());
ipi.setSpecification(ip.getSpecification());
PrimeOrderGroup Gq = ip.getGroup();
ipi.setGroup(Gq);
ipi.setProverIssuanceValues(Gq.getElementArray(ip.getProverIssuanceValues()));
ipi.setPublicKey(Gq.getElementArray(ip.getPublicKey()));
return ipi;
}
}