//*********************************************************
//
// 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.util.Arrays;
import com.microsoft.uprove.FieldZq.ZqElement;
import com.microsoft.uprove.ProtocolHelper.GenerateChallengeOutput;
/**
* Class to generate and verify presentation proofs.
*/
public class PresentationProtocol {
/**
* Private constructor to prevent instantiation.
*/
private PresentationProtocol() {
super();
}
/**
* Generates a presentation proof.
* @param ip the issuer parameters under which the U-Prove token was issued.
* @param disclosed the ordered list of disclosed token attribute indices.
* @param m the protocol message.
* @param md the protocol message for Device.
* @param upkt the U-Prove key and token to use.
* @param attributes the list of all token attributes.
* @return a presentation proof.
* @throws IOException if an argument is malformed.
*/
public static PresentationProof generatePresentationProof(IssuerParameters ip, int[] disclosed, byte[] m, byte[] md, UProveKeyAndToken upkt, byte[][] attributes) throws IOException {
return generatePresentationProof(ip, disclosed, m, md, upkt, attributes, null);
}
/**
* Generates a presentation proof.
* @param ip the issuer parameters under which the U-Prove token was issued.
* @param disclosed the ordered list of disclosed token attribute indices.
* @param m the protocol message.
* @param md the protocol message for Device.
* @param upkt the U-Prove key and token to use.
* @param attributes the list of all token attributes.
* @param preGenW a list of pre-generated <code>w</code> values.
* @return a presentation proof.
* @throws IOException if an argument is malformed.
*/
public static PresentationProof generatePresentationProof(IssuerParameters ip, int[] disclosed, byte[] m, byte[] md, UProveKeyAndToken upkt, byte[][] attributes, byte[][] preGenW) throws IOException {
IssuerParametersInternal ipi = IssuerParametersInternal.generate(ip);
UProveTokenInternal upti = UProveTokenInternal.generate(ipi, upkt.getToken());
PrimeOrderGroup Gq = ipi.getGroup();
FieldZq Zq = Gq.getZq();
ZqElement[] x = ProtocolHelper.computeXArray(ipi, attributes, upti.getTokenInformation());
int n = ipi.getEncodingBytes().length;
int nUndisclosed = n - disclosed.length;
int numRandomizer = nUndisclosed + 1 + (upti.isDeviceProtected() ? 1 : 0);
int[] undisclosed = ProtocolHelper.getUndisclosedIndices(n, disclosed);
ZqElement[] w;
if (preGenW == null) {
w = Zq.getRandomElements(numRandomizer, false);
} else {
if (preGenW.length != numRandomizer) {
throw new IllegalArgumentException("Expected size for preGenW is " + nUndisclosed + ", actual size is " + preGenW.length);
}
w = ProtocolHelper.getZqElementArray(Zq, preGenW);
}
GroupElement[] bases = new GroupElement[numRandomizer];
bases[0] = upti.getPublicKey();
int bIndex = 1;
GroupElement[] g = ipi.getPublicKey();
for (int i=0; i<nUndisclosed; i++) {
bases[bIndex++] = g[undisclosed[i]];
}
if (upti.isDeviceProtected()) {
bases[bIndex++] = ipi.getDeviceGenerator();
}
GroupElement temp = ProtocolHelper.computeProduct(bases, w);
if (upti.isDeviceProtected()) {
temp.multiplyAssign(Gq.getElement(DeviceManager.GetInitialWitness()));
}
HashFunction H = ipi.getHashFunction();
H.update(temp);
byte[] a = H.getByteDigest();
byte[][] disclosedAttributes = new byte[disclosed.length][];
ZqElement[] disclosedX = new ZqElement[disclosed.length];
for (int i=0; i<disclosed.length; i++) {
disclosedAttributes[i] = attributes[disclosed[i]-1]; // attributes array is zero-based
disclosedX[i] = x[disclosed[i]];
}
GenerateChallengeOutput gco = ProtocolHelper.genChallenge(ipi, upti, a, m, md, disclosed, disclosedX);
ZqElement c = gco.getC();
ZqElement r0 = c.multiply(Zq.getPositiveElement(upkt.getTokenPrivateKey())).add(w[0]);
ZqElement[] r = new ZqElement[nUndisclosed];
for (int i=0; i<nUndisclosed; i++) {
r[i] = c.negate().multiply(x[undisclosed[i]]).add(w[i+1]);
}
ZqElement rd = null;
if (upti.isDeviceProtected()) {
rd = Zq.getPositiveElement(DeviceManager.GetDeviceResponse(md, gco.getMdPrime())).add(w[numRandomizer-1]);
}
return new PresentationProof(disclosedAttributes, a, r0.toByteArray(), ProtocolHelper.getEncodedArray(r), upti.isDeviceProtected() ? rd.toByteArray() : null);
}
/**
* Verifies a presentation proof.
* @param ip the issuer parameters under which the U-Prove token was issued.
* @param disclosed the ordered list of disclosed token attribute indices.
* @param m the protocol message.
* @param upt the U-Prove token.
* @param pp the presentation proof.
* @throws InvalidProofException if the proof is invalid.
* @throws IOException if an argument is malformed.
*/
public static void verifyPresentationProof(IssuerParameters ip, int[] disclosed, byte[] m, byte[] md, UProveToken upt, PresentationProof pp) throws InvalidProofException, IOException {
IssuerParametersInternal ipi = IssuerParametersInternal.generate(ip);
UProveTokenInternal upti = UProveTokenInternal.generate(ipi, upt);
// arg validation
if (disclosed.length != pp.getDisclosedAttributes().length) {
throw new InvalidProofException("Mismatch in number of disclosed attributes");
}
if (!Arrays.equals(upti.getIssuerParametersUID(), ipi.getParametersUID())) {
throw new IllegalArgumentException("Issuer parameters UID does not match the one referenced in the token.");
}
if (!ProtocolHelper.isTokenSignatureValid(ipi, upti)) {
throw new InvalidProofException("token signature is invalid.");
}
// [1, x_d_1, ..., x_d_k, x_t]
ZqElement[] disclosedX = ProtocolHelper.computeXArray(ipi, disclosed, pp.getDisclosedAttributes(), upti.getTokenInformation());
ZqElement c = ProtocolHelper.genChallenge(ipi, upti, pp.getA(), m, md, disclosed, (ZqElement[]) Arrays.copyOfRange(disclosedX, 1, disclosedX.length-1)).getC();
GroupElement[] g = ipi.getPublicKey();
int tIndex = g.length - (upti.isDeviceProtected() ? 2 : 1);
GroupElement[] disclosedG = new GroupElement[disclosed.length + 2];
// g_0
disclosedG[0] = g[0];
// g_t
disclosedG[disclosedG.length-1] = g[tIndex];
for (int i=0; i<disclosed.length; i++) {
disclosedG[i+1] = g[disclosed[i]];
}
GroupElement temp1 = ProtocolHelper.computeProduct(disclosedG, disclosedX).exponentiate(c.negate());
PrimeOrderGroup Gq = ipi.getGroup();
FieldZq Zq = Gq.getZq();
int[] undisclosed = ProtocolHelper.getUndisclosedIndices(ipi.getEncodingBytes().length, disclosed);
int basesLength = undisclosed.length + (upti.isDeviceProtected() ? 2 : 1);
GroupElement[] bases = new GroupElement[basesLength];
bases[0] = upti.getPublicKey();
for (int i=0; i<undisclosed.length; i++) {
bases[i+1] = g[undisclosed[i]];
}
ZqElement[] combinedR = new ZqElement[basesLength];
int index = 0;
combinedR[index++] = Zq.getPositiveElement(pp.getR0());
ZqElement r[] = ProtocolHelper.getZqElementArray(Zq, pp.getR());
for (int i=0; i<r.length; i++) {
combinedR[index++] = r[i];
}
if (upti.isDeviceProtected()) {
bases[basesLength-1] = ipi.getDeviceGenerator();
combinedR[index++] = Zq.getPositiveElement(pp.getRd());
}
GroupElement temp2 = ProtocolHelper.computeProduct(bases, combinedR);
HashFunction H = ipi.getHashFunction();
GroupElement hashInput = temp1.multiply(temp2);
if (upti.isDeviceProtected()) {
hashInput.multiply(ipi.getDeviceGenerator().exponentiate(Zq.getPositiveElement(pp.getRd())));
}
H.update(hashInput);
byte[] a = H.getByteDigest();
if (!Arrays.equals(pp.getA(), a)) {
throw new InvalidProofException("proof is invalid.");
}
}
}