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.
/** * XMSS. */
public class XMSS {
XMSS parameters.
/** * XMSS parameters. */
private final XMSSParameters params;
WOTS+ instance.
/** * WOTS+ instance. */
private WOTSPlus wotsPlus;
PRNG.
/** * PRNG. */
private SecureRandom prng;
XMSS private key.
/** * XMSS private key. */
private XMSSPrivateKeyParameters privateKey;
XMSS public key.
/** * XMSS public key. */
private XMSSPublicKeyParameters publicKey;
XMSS constructor...
Params:
  • params – XMSSParameters.
/** * 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.
/** * 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.
Params:
  • privateKey – XMSS private key.
  • publicKey – XMSS public key.
/** * 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.
Params:
  • message – Message to sign.
Returns:XMSS signature on digest of message.
/** * 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.
Params:
  • message – Message.
  • signature – XMSS signature.
  • publicKey – XMSS public key.
Throws:
Returns:true if signature is valid false else.
/** * 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.
Returns:XMSS private key.
/** * Export XMSS private key. * * @return XMSS private key. */
public byte[] exportPrivateKey() { return privateKey.toByteArray(); }
Export XMSS public key.
Returns:XMSS public key.
/** * 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
Params:
  • messageDigest – Message digest of length n.
  • otsHashAddress – OTS hash address.
Returns:XMSS signature.
/** * 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.
Returns:XMSS params.
/** * Getter XMSS params. * * @return XMSS params. */
public XMSSParameters getParams() { return params; }
Getter WOTS+.
Returns:WOTS+ instance.
/** * Getter WOTS+. * * @return WOTS+ instance. */
protected WOTSPlus getWOTSPlus() { return wotsPlus; }
Getter XMSS root.
Returns:Root of binary tree.
/** * 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.
Returns:Index.
/** * 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.
Returns:Public seed.
/** * 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; } }