blob: c765237171c8c62e90f3d9fb3bdf1a4b615a2f0f [file] [log] [blame]
package org.bouncycastle.pqc.crypto.xmss;
import java.security.SecureRandom;
import java.text.ParseException;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.util.Arrays;
/**
* XMSS.
*/
public class XMSS
{
/**
* XMSS parameters.
*/
private final XMSSParameters params;
/**
* WOTS+ instance.
*/
private WOTSPlus wotsPlus;
/**
* PRNG.
*/
private SecureRandom prng;
/**
* XMSS private key.
*/
private XMSSPrivateKeyParameters privateKey;
/**
* XMSS public key.
*/
private XMSSPublicKeyParameters publicKey;
/**
* XMSS constructor...
*
* @param params XMSSParameters.
*/
public XMSS(XMSSParameters params, SecureRandom prng)
{
super();
if (params == null)
{
throw new NullPointerException("params == null");
}
this.params = params;
wotsPlus = params.getWOTSPlus();
this.prng = prng;
}
// public void generateKeys()
// {
// /* generate private key */
// privateKey = generatePrivateKey(params, prng);
// XMSSNode root = privateKey.getBDSState().initialize(privateKey, (OTSHashAddress)new OTSHashAddress.Builder().build());
//
// privateKey = new XMSSPrivateKeyParameters.Builder(params).withIndex(privateKey.getIndex())
// .withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
// .withPublicSeed(privateKey.getPublicSeed()).withRoot(root.getValue())
// .withBDSState(privateKey.getBDSState()).build();
// publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root.getValue())
// .withPublicSeed(getPublicSeed()).build();
//
// }
//
// /**
// * Generate an XMSS private key.
// *
// * @return XMSS private key.
// */
// private XMSSPrivateKeyParameters generatePrivateKey(XMSSParameters params, SecureRandom prng)
// {
// int n = params.getDigestSize();
// byte[] secretKeySeed = new byte[n];
// prng.nextBytes(secretKeySeed);
// byte[] secretKeyPRF = new byte[n];
// prng.nextBytes(secretKeyPRF);
// byte[] publicSeed = new byte[n];
// prng.nextBytes(publicSeed);
//
// XMSS xmss = new XMSS(params, prng);
//
//// this.privateKey = xmss.privateKey;
//// this.publicKey = xmss.publicKey;
//// this.wotsPlus = xmss.wotsPlus;
//// this.khf = xmss.khf;
//
// XMSSPrivateKeyParameters privateKey = new XMSSPrivateKeyParameters.Builder(params).withSecretKeySeed(secretKeySeed)
// .withSecretKeyPRF(secretKeyPRF).withPublicSeed(publicSeed)
// .withBDSState(new BDS(xmss)).build();
//
// return privateKey;
// }
/**
* Generate a new XMSS private key / public key pair.
*/
public void generateKeys()
{
XMSSKeyPairGenerator kpGen = new XMSSKeyPairGenerator();
kpGen.init(new XMSSKeyGenerationParameters(getParams(), prng));
AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();
privateKey = (XMSSPrivateKeyParameters)kp.getPrivate();
publicKey = (XMSSPublicKeyParameters)kp.getPublic();
wotsPlus.importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
}
void importState(XMSSPrivateKeyParameters privateKey, XMSSPublicKeyParameters publicKey)
{
if (!Arrays.areEqual(privateKey.getRoot(), publicKey.getRoot()))
{
throw new IllegalStateException("root of private key and public key do not match");
}
if (!Arrays.areEqual(privateKey.getPublicSeed(), publicKey.getPublicSeed()))
{
throw new IllegalStateException("public seed of private key and public key do not match");
}
/* import */
this.privateKey = privateKey;
this.publicKey = publicKey;
wotsPlus.importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
}
/**
* Import XMSS private key / public key pair.
*
* @param privateKey XMSS private key.
* @param publicKey XMSS public key.
*/
public void importState(byte[] privateKey, byte[] publicKey)
{
if (privateKey == null)
{
throw new NullPointerException("privateKey == null");
}
if (publicKey == null)
{
throw new NullPointerException("publicKey == null");
}
/* import keys */
XMSSPrivateKeyParameters tmpPrivateKey = new XMSSPrivateKeyParameters.Builder(params)
.withPrivateKey(privateKey, this.getParams()).build();
XMSSPublicKeyParameters tmpPublicKey = new XMSSPublicKeyParameters.Builder(params).withPublicKey(publicKey)
.build();
if (!Arrays.areEqual(tmpPrivateKey.getRoot(), tmpPublicKey.getRoot()))
{
throw new IllegalStateException("root of private key and public key do not match");
}
if (!Arrays.areEqual(tmpPrivateKey.getPublicSeed(), tmpPublicKey.getPublicSeed()))
{
throw new IllegalStateException("public seed of private key and public key do not match");
}
/* import */
this.privateKey = tmpPrivateKey;
this.publicKey = tmpPublicKey;
wotsPlus.importKeys(new byte[params.getDigestSize()], this.privateKey.getPublicSeed());
}
/**
* Sign message.
*
* @param message Message to sign.
* @return XMSS signature on digest of message.
*/
public byte[] sign(byte[] message)
{
if (message == null)
{
throw new NullPointerException("message == null");
}
XMSSSigner signer = new XMSSSigner();
signer.init(true, privateKey);
byte[] signature = signer.generateSignature(message);
privateKey = (XMSSPrivateKeyParameters)signer.getUpdatedPrivateKey();
importState(privateKey, publicKey);
return signature;
}
/**
* Verify an XMSS signature.
*
* @param message Message.
* @param signature XMSS signature.
* @param publicKey XMSS public key.
* @return true if signature is valid false else.
* @throws ParseException
*/
public boolean verifySignature(byte[] message, byte[] signature, byte[] publicKey)
throws ParseException
{
if (message == null)
{
throw new NullPointerException("message == null");
}
if (signature == null)
{
throw new NullPointerException("signature == null");
}
if (publicKey == null)
{
throw new NullPointerException("publicKey == null");
}
XMSSSigner signer = new XMSSSigner();
signer.init(false, new XMSSPublicKeyParameters.Builder(getParams()).withPublicKey(publicKey).build());
return signer.verifySignature(message, signature);
}
/**
* Export XMSS private key.
*
* @return XMSS private key.
*/
public byte[] exportPrivateKey()
{
return privateKey.toByteArray();
}
/**
* Export XMSS public key.
*
* @return XMSS public key.
*/
public byte[] exportPublicKey()
{
return publicKey.toByteArray();
}
/**
* Generate a WOTS+ signature on a message without the corresponding
* authentication path
*
* @param messageDigest Message digest of length n.
* @param otsHashAddress OTS hash address.
* @return XMSS signature.
*/
protected WOTSPlusSignature wotsSign(byte[] messageDigest, OTSHashAddress otsHashAddress)
{
if (messageDigest.length != params.getDigestSize())
{
throw new IllegalArgumentException("size of messageDigest needs to be equal to size of digest");
}
if (otsHashAddress == null)
{
throw new NullPointerException("otsHashAddress == null");
}
/* (re)initialize WOTS+ instance */
wotsPlus.importKeys(wotsPlus.getWOTSPlusSecretKey(privateKey.getSecretKeySeed(), otsHashAddress), getPublicSeed());
/* create WOTS+ signature */
return wotsPlus.sign(messageDigest, otsHashAddress);
}
/**
* Getter XMSS params.
*
* @return XMSS params.
*/
public XMSSParameters getParams()
{
return params;
}
/**
* Getter WOTS+.
*
* @return WOTS+ instance.
*/
protected WOTSPlus getWOTSPlus()
{
return wotsPlus;
}
/**
* Getter XMSS root.
*
* @return Root of binary tree.
*/
public byte[] getRoot()
{
return privateKey.getRoot();
}
protected void setRoot(byte[] root)
{
privateKey = new XMSSPrivateKeyParameters.Builder(params)
.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
.withPublicSeed(getPublicSeed()).withRoot(root).withBDSState(privateKey.getBDSState()).build();
publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(root).withPublicSeed(getPublicSeed())
.build();
}
/**
* Getter XMSS index.
*
* @return Index.
*/
public int getIndex()
{
return privateKey.getIndex();
}
protected void setIndex(int index)
{
privateKey = new XMSSPrivateKeyParameters.Builder(params)
.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
.withPublicSeed(privateKey.getPublicSeed()).withRoot(privateKey.getRoot())
.withBDSState(privateKey.getBDSState()).build();
}
/**
* Getter XMSS public seed.
*
* @return Public seed.
*/
public byte[] getPublicSeed()
{
return privateKey.getPublicSeed();
}
protected void setPublicSeed(byte[] publicSeed)
{
privateKey = new XMSSPrivateKeyParameters.Builder(params)
.withSecretKeySeed(privateKey.getSecretKeySeed()).withSecretKeyPRF(privateKey.getSecretKeyPRF())
.withPublicSeed(publicSeed).withRoot(getRoot()).withBDSState(privateKey.getBDSState()).build();
publicKey = new XMSSPublicKeyParameters.Builder(params).withRoot(getRoot()).withPublicSeed(publicSeed)
.build();
wotsPlus.importKeys(new byte[params.getDigestSize()], publicSeed);
}
public XMSSPrivateKeyParameters getPrivateKey()
{
return privateKey;
}
}