blob: 451422e37104ad78acb028d1b5092ab9e7a81cd4 [file] [log] [blame]
package org.bouncycastle.pqc.crypto.ntru;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.pqc.math.ntru.polynomial.DenseTernaryPolynomial;
import org.bouncycastle.pqc.math.ntru.polynomial.IntegerPolynomial;
import org.bouncycastle.pqc.math.ntru.polynomial.Polynomial;
import org.bouncycastle.pqc.math.ntru.polynomial.ProductFormPolynomial;
import org.bouncycastle.pqc.math.ntru.polynomial.SparseTernaryPolynomial;
/**
* A NtruSign private key comprises one or more {@link NTRUSigningPrivateKeyParameters.Basis} of three polynomials each,
* except the zeroth basis for which <code>h</code> is undefined.
*/
public class NTRUSigningPrivateKeyParameters
extends AsymmetricKeyParameter
{
private List<Basis> bases;
private NTRUSigningPublicKeyParameters publicKey;
/**
* Constructs a new private key from a byte array
*
* @param b an encoded private key
* @param params the NtruSign parameters to use
*/
public NTRUSigningPrivateKeyParameters(byte[] b, NTRUSigningKeyGenerationParameters params)
throws IOException
{
this(new ByteArrayInputStream(b), params);
}
/**
* Constructs a new private key from an input stream
*
* @param is an input stream
* @param params the NtruSign parameters to use
*/
public NTRUSigningPrivateKeyParameters(InputStream is, NTRUSigningKeyGenerationParameters params)
throws IOException
{
super(true);
bases = new ArrayList<Basis>();
for (int i = 0; i <= params.B; i++)
// include a public key h[i] in all bases except for the first one
{
add(new Basis(is, params, i != 0));
}
publicKey = new NTRUSigningPublicKeyParameters(is, params.getSigningParameters());
}
public NTRUSigningPrivateKeyParameters(List<Basis> bases, NTRUSigningPublicKeyParameters publicKey)
{
super(true);
this.bases = new ArrayList<Basis>(bases);
this.publicKey = publicKey;
}
/**
* Adds a basis to the key.
*
* @param b a NtruSign basis
*/
private void add(Basis b)
{
bases.add(b);
}
/**
* Returns the <code>i</code>-th basis
*
* @param i the index
* @return the basis at index <code>i</code>
*/
public Basis getBasis(int i)
{
return bases.get(i);
}
public NTRUSigningPublicKeyParameters getPublicKey()
{
return publicKey;
}
/**
* Converts the key to a byte array
*
* @return the encoded key
*/
public byte[] getEncoded()
throws IOException
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
for (int i = 0; i < bases.size(); i++)
{
// all bases except for the first one contain a public key
bases.get(i).encode(os, i != 0);
}
os.write(publicKey.getEncoded());
return os.toByteArray();
}
/**
* Writes the key to an output stream
*
* @param os an output stream
* @throws IOException
*/
public void writeTo(OutputStream os)
throws IOException
{
os.write(getEncoded());
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result;
if (bases==null) return result;
result += bases.hashCode();
for (Basis basis : bases)
{
result += basis.hashCode();
}
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
NTRUSigningPrivateKeyParameters other = (NTRUSigningPrivateKeyParameters)obj;
if ((bases == null) != (other.bases == null))
{
return false;
}
if (bases == null)
{
return true;
}
if (bases.size() != other.bases.size())
{
return false;
}
for (int i = 0; i < bases.size(); i++)
{
Basis basis1 = bases.get(i);
Basis basis2 = other.bases.get(i);
if (!basis1.f.equals(basis2.f))
{
return false;
}
if (!basis1.fPrime.equals(basis2.fPrime))
{
return false;
}
if (i != 0 && !basis1.h.equals(basis2.h)) // don't compare h for the 0th basis
{
return false;
}
if (!basis1.params.equals(basis2.params))
{
return false;
}
}
return true;
}
/**
* A NtruSign basis. Contains three polynomials <code>f, f', h</code>.
*/
public static class Basis
{
public Polynomial f;
public Polynomial fPrime;
public IntegerPolynomial h;
NTRUSigningKeyGenerationParameters params;
/**
* Constructs a new basis from polynomials <code>f, f', h</code>.
*
* @param f
* @param fPrime
* @param h
* @param params NtruSign parameters
*/
protected Basis(Polynomial f, Polynomial fPrime, IntegerPolynomial h, NTRUSigningKeyGenerationParameters params)
{
this.f = f;
this.fPrime = fPrime;
this.h = h;
this.params = params;
}
/**
* Reads a basis from an input stream and constructs a new basis.
*
* @param is an input stream
* @param params NtruSign parameters
* @param include_h whether to read the polynomial <code>h</code> (<code>true</code>) or only <code>f</code> and <code>f'</code> (<code>false</code>)
*/
Basis(InputStream is, NTRUSigningKeyGenerationParameters params, boolean include_h)
throws IOException
{
int N = params.N;
int q = params.q;
int d1 = params.d1;
int d2 = params.d2;
int d3 = params.d3;
boolean sparse = params.sparse;
this.params = params;
if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
{
f = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
}
else
{
IntegerPolynomial fInt = IntegerPolynomial.fromBinary3Tight(is, N);
f = sparse ? new SparseTernaryPolynomial(fInt) : new DenseTernaryPolynomial(fInt);
}
if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
{
IntegerPolynomial fPrimeInt = IntegerPolynomial.fromBinary(is, N, q);
for (int i = 0; i < fPrimeInt.coeffs.length; i++)
{
fPrimeInt.coeffs[i] -= q / 2;
}
fPrime = fPrimeInt;
}
else if (params.polyType == NTRUParameters.TERNARY_POLYNOMIAL_TYPE_PRODUCT)
{
fPrime = ProductFormPolynomial.fromBinary(is, N, d1, d2, d3 + 1, d3);
}
else
{
fPrime = IntegerPolynomial.fromBinary3Tight(is, N);
}
if (include_h)
{
h = IntegerPolynomial.fromBinary(is, N, q);
}
}
/**
* Writes the basis to an output stream
*
* @param os an output stream
* @param include_h whether to write the polynomial <code>h</code> (<code>true</code>) or only <code>f</code> and <code>f'</code> (<code>false</code>)
* @throws IOException
*/
void encode(OutputStream os, boolean include_h)
throws IOException
{
int q = params.q;
os.write(getEncoded(f));
if (params.basisType == NTRUSigningKeyGenerationParameters.BASIS_TYPE_STANDARD)
{
IntegerPolynomial fPrimeInt = fPrime.toIntegerPolynomial();
for (int i = 0; i < fPrimeInt.coeffs.length; i++)
{
fPrimeInt.coeffs[i] += q / 2;
}
os.write(fPrimeInt.toBinary(q));
}
else
{
os.write(getEncoded(fPrime));
}
if (include_h)
{
os.write(h.toBinary(q));
}
}
private byte[] getEncoded(Polynomial p)
{
if (p instanceof ProductFormPolynomial)
{
return ((ProductFormPolynomial)p).toBinary();
}
else
{
return p.toIntegerPolynomial().toBinary3Tight();
}
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((f == null) ? 0 : f.hashCode());
result = prime * result + ((fPrime == null) ? 0 : fPrime.hashCode());
result = prime * result + ((h == null) ? 0 : h.hashCode());
result = prime * result + ((params == null) ? 0 : params.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (!(obj instanceof Basis))
{
return false;
}
Basis other = (Basis)obj;
if (f == null)
{
if (other.f != null)
{
return false;
}
}
else if (!f.equals(other.f))
{
return false;
}
if (fPrime == null)
{
if (other.fPrime != null)
{
return false;
}
}
else if (!fPrime.equals(other.fPrime))
{
return false;
}
if (h == null)
{
if (other.h != null)
{
return false;
}
}
else if (!h.equals(other.h))
{
return false;
}
if (params == null)
{
if (other.params != null)
{
return false;
}
}
else if (!params.equals(other.params))
{
return false;
}
return true;
}
}
}