//*********************************************************
//
// 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;
/**
* Implements the subgroup construction, which is a
* subgroup of prime order <code>q</code> of
* Z<code><sub>p</sub><sup>*</sup></code>.
*/
public final class Subgroup extends PrimeOrderGroup {
/**
* The group's <code>p</code> prime modulus.
*/
private final BigInteger p;
/**
* The group's generator.
*/
private final BigInteger g;
/**
* Constructs a new <code>Subgroup</code>.
* @param p the encoded <code>p</code> value.
* @param q the encoded <code>q</code> value.
* @param g the encoded <code>g</code> value.
*/
public Subgroup(final byte[] p, final byte[] q, final byte[] g) {
super(new BigInteger(1, q));
this.p = new BigInteger(1, p);
this.g = new BigInteger(1, g);
validatePQG();
}
/**
* Constructs the group <code>Subgroup</code> of prime order q.
* Note that <code>p</code> and <code>q</code> should be prime numbers,
* <code>q</code> should divide <code>(p-1)</code>, and <code>g</code>
* should be a group member different than 1 (and therefore a generator).
* None of these checks are performed by the constructor, as they are
* expensive operations and should only be performed once (not every time
* the values are used to instantiate a <code>Subgroup</code>
* object). The method {@link #validate()} can be used to perform such a
* validation.
* @param p the group's prime modulus <code>p</code>.
* @param q the group's prime order <code>q</code>.
* @param g the group's generator <code>g</code>.
* @throws IllegalArgumentException if <code>p</code>, <code>q</code>
* or <code>g</code> are not greater than 1 or if <code>g</code> is not
* less than <code>p</code>.
*/
public Subgroup(final BigInteger p, final BigInteger q, final BigInteger g) {
super(q);
this.p = p;
this.g = g;
validatePQG();
}
private void validatePQG() {
// the PrimeOrderGroup constructor verifies that q is > 1
// check that p > 1
if (p.compareTo(BigInteger.ONE) <= 0) {
throw new IllegalArgumentException(
"Invalid value for p (smaller or equal to 1)");
}
// check that 1 < g < p
if (g.compareTo(BigInteger.ONE) <= 0) {
throw new IllegalArgumentException(
"Invalid value for g (smaller or equal to 1)");
}
if (g.compareTo(p) >= 0) {
throw new IllegalArgumentException(
"Invalid value for g (bigger or equal to p)");
}
}
/**
* Returns a big integer representing an element's value.
* @param element an element in a <code>Subgroup</code>.
* @return a big integer representing the element's value.
* @throws ClassCastException if <code>element</code> is not an element
* of a <code>Subgroup</code>.
*/
public static BigInteger getElementValue(final GroupElement element)
throws ClassCastException {
return ((ModInteger) element).toBigInteger();
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.PrimeOrderGroup#getIdentity()
*/
public GroupElement getIdentity() {
return new ModInteger(BigInteger.ONE);
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.PrimeOrderGroup#getGenerator()
*/
public GroupElement getGenerator() {
return new ModInteger(g);
}
/**
* Returns the parameter <code>p</code> of the group as a BigInteger.
* @return the parameter <code>p</code> of the group as a BigInteger.
*/
public BigInteger getP() {
return p;
}
/**
* Returns the parameter <code>q</code> of the group as a BigInteger.
* @return the parameter <code>q</code> of the group as a BigInteger.
*/
public BigInteger getQ() {
return getOrder();
}
/**
* Returns the parameter <code>g</code> of the group as a BigInteger.
* @return the parameter <code>g</code> of the group as a BigInteger.
*/
public BigInteger getG() {
return g;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.PrimeOrderGroup#getElement(byte[])
*/
public GroupElement getElement(final byte[] data)
throws IOException {
try {
return new ModInteger(SubgroupUtil
.decodeElementValue(data));
} catch (IllegalArgumentException iae) {
throw new IOException(iae.getMessage());
}
}
/**
* Checks that the given integer belongs to the group, i.e.
* checks that <code>0 < i < p</code> and that
* <code>i^q (mod p) = 1</code>.
* @param i the <code>BigInteger</code> to test.
* @return <code>true</code> if the integer belongs to the group,
* <code>false</code> otherwise.
*/
boolean isValidElementValue(final BigInteger i) {
return
// check that i > 0
i.signum() > 0
// check that i < p
&& i.compareTo(p) < 0
// check that i^q (mod p) == 1
&& i.modPow(getOrder(), p).equals(BigInteger.ONE);
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.PrimeOrderGroup#getMaxEncodedElementSize()
*/
public int getMaxEncodedElementSize() {
return SubgroupUtil.getEncodedElementSize(p.bitLength());
}
/**
* Indicates whether some other object is "equal to" this one.
* @param o the reference object with which to compare.
* @return <code>true</code> if this object is the same as the
* <code>o</code> argument; <code>false</code> otherwise.
*/
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Subgroup) || !super.equals(o)) {
return false;
}
final Subgroup G = (Subgroup) o;
return p.equals(G.p) && g.equals(G.g);
}
/**
* Returns a hash code value for the object.
* @return a hash code value for the object.
*/
public int hashCode() {
int retVal = 83;
retVal = retVal * 593 + p.hashCode();
retVal = retVal * 593 + super.hashCode();
retVal = retVal * 593 + g.hashCode();
return retVal;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.Hashable#addToDigest(com.microsoft.uprove.crypto.HashUpdater)
*/
public void addToDigest(final HashUpdater dv) {
dv.update(p);
dv.update(getOrder()); // q
dv.update(g);
}
/**
* Represents an integer of a Subgroup.
*/
private final class ModInteger implements GroupElement {
/*
* Note that this class uses an immutable BigInteger, which is not
* ideal for fast crypto operations and to erase the content.
* We plan to migrate to a mutable big integer class eventually.
*/
private BigInteger integer;
/**
* Constructs a ModInteger. The constructor simply checks that
* the integer is between zero and <code>p</code>, exclusive;
* not that it has order <code>q</code> (i.e. that
* <code>integer^q = 1</code>), since this is an expensive
* operation. Therefore, it's possible to construct a
* modular integer that is not part of the group. The method
* {@link GroupElement#isValid()} can be used to verify group
* membership.
* @param integer integer value of the element.
* @throws IllegalArgumentException if integer is smaller or
* equal to 0, or is bigger or equal to <code>p</code>.
* @see GroupElement#isValid()
*/
ModInteger(final BigInteger integer) {
if (integer.signum() <= 0 || integer.compareTo(p) >= 0) {
throw new IllegalArgumentException(
"Integer not in group range: " + integer);
}
this.integer = integer;
}
/**
* @return a <code>BigInteger</code> representation of this
* <code>ModInteger</code>.
*/
public BigInteger toBigInteger() {
return integer;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.Element#length()
*/
public int length() {
return SubgroupUtil.getEncodedElementSize(integer.bitLength());
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#toByteArray()
*/
public byte[] toByteArray() {
return SubgroupUtil.encodeElementValue(integer);
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#getGroup()
*/
public PrimeOrderGroup getGroup() {
return Subgroup.this;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#multiply(com.microsoft.uprove.math.GroupElement)
*/
public GroupElement multiply(final GroupElement b) {
if (!b.getGroup().equals(Subgroup.this)) {
throw new IllegalArgumentException("b is not in same group");
}
return new ModInteger(integer.multiply(((ModInteger) b).integer)
.mod(p));
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#multiplyAssign(com.microsoft.uprove.math.GroupElement)
*/
public GroupElement multiplyAssign(final GroupElement b) {
if (!b.getGroup().equals(Subgroup.this)) {
throw new IllegalArgumentException("b is not in same group");
}
integer = integer.multiply(((ModInteger) b).integer).mod(p);
return this;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#exponentiate(com.microsoft.uprove.math.FieldZq.ZqElement)
*/
public GroupElement exponentiate(final FieldZq.ZqElement n) {
return new ModInteger(integer.modPow(n.toBigInteger(), p));
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#exponentiateAssign(com.microsoft.uprove.math.FieldZq.ZqElement)
*/
public GroupElement exponentiateAssign(final FieldZq.ZqElement n) {
integer = integer.modPow(n.toBigInteger(), p);
return this;
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#inverse()
*/
public GroupElement inverse() {
return new ModInteger(integer.modInverse(p));
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#inverseAssign()
*/
public GroupElement inverseAssign() {
integer = integer.modInverse(p);
return this;
}
/**
* Returns the hexadecimal representation of this ModInteger.
* @return the hexadecimal representation of this ModInteger.
*/
public String toString() {
return integer.toString(16);
}
/**
* Indicates whether some other object is "equal to" this one.
* @param o the reference object with which to compare.
* @return <code>true</code> if this object is the same as the
* <code>o</code> argument; <code>false</code> otherwise.
*/
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof ModInteger)) {
return false;
}
ModInteger mi = (ModInteger) o;
return integer.equals(mi.integer);
}
/**
* Returns a hash code value for the object.
* @return a hash code value for the object.
*/
public int hashCode() {
return integer.hashCode();
}
/* (non-Javadoc)
* @see com.microsoft.uprove.crypto.Hashable#addToDigest(com.microsoft.uprove.crypto.HashUpdater)
*/
public void addToDigest(final HashUpdater dv) {
dv.update(integer);
}
/* (non-Javadoc)
* @see com.microsoft.uprove.math.GroupElement#isValid()
*/
public boolean isValid() {
return Subgroup.this.isValidElementValue(integer);
}
}
/**
* Validates that the group is well-formed, i.e. that
* the order p is a prime number, that q | (p - 1),
* that g is not equal to 1 and is a member of the group (has order q).
* @throws IllegalStateException if the group is malformed.
*/
protected void doGroupSpecificValidate() throws IllegalStateException {
// the constructor ensures that p and g are in range
// verify that p is prime
if (!p.isProbablePrime(ConfigImpl.getPrimeConfidenceLevel())) {
throw new IllegalStateException(
"Invalid value for p (not a prime)");
}
// verification that q is prime is done while validating Zq
final BigInteger q = getOrder();
// verify that q | (p - 1)
if (p.subtract(BigInteger.ONE).remainder(q).signum() != 0) {
throw new IllegalStateException(
"Invalid value for p and q, q does not divide p - 1");
}
// 1 < g < p is checked by constructor (g is final, so no danger)
// verify that g^q == 1 to make sure the generator is in the group
if (g.modPow(q, p).compareTo(BigInteger.ONE) != 0) {
throw new IllegalStateException(
"Invalid value for g (not of order q)");
}
}
}