| package org.bouncycastle.pqc.crypto.xmss; |
| |
| import java.io.IOException; |
| import java.text.ParseException; |
| |
| import org.bouncycastle.util.Pack; |
| |
| /** |
| * XMSS Private Key. |
| * |
| */ |
| public final class XMSSPrivateKeyParameters implements XMSSStoreableObjectInterface { |
| |
| /** |
| * XMSS parameters object. |
| */ |
| private final XMSSParameters params; |
| /** |
| * Index for WOTS+ keys (randomization factor). |
| */ |
| private final int index; |
| /** |
| * Secret for the derivation of WOTS+ secret keys. |
| */ |
| private final byte[] secretKeySeed; |
| /** |
| * Secret for the randomization of message digests during signature |
| * creation. |
| */ |
| private final byte[] secretKeyPRF; |
| /** |
| * Public seed for the randomization of hashes. |
| */ |
| private final byte[] publicSeed; |
| /** |
| * Public root of binary tree. |
| */ |
| private final byte[] root; |
| /** |
| * BDS state. |
| */ |
| private final BDS bdsState; |
| |
| private XMSSPrivateKeyParameters(Builder builder) throws ParseException, ClassNotFoundException, IOException { |
| super(); |
| params = builder.params; |
| if (params == null) { |
| throw new NullPointerException("params == null"); |
| } |
| int n = params.getDigestSize(); |
| byte[] privateKey = builder.privateKey; |
| if (privateKey != null) { |
| if (builder.xmss == null) { |
| throw new NullPointerException("xmss == null"); |
| } |
| /* import */ |
| int height = params.getHeight(); |
| int indexSize = 4; |
| int secretKeySize = n; |
| int secretKeyPRFSize = n; |
| int publicSeedSize = n; |
| int rootSize = n; |
| /* |
| int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize; |
| if (privateKey.length != totalSize) { |
| throw new ParseException("private key has wrong size", 0); |
| } |
| */ |
| int position = 0; |
| index = Pack.bigEndianToInt(privateKey, position); |
| if (!XMSSUtil.isIndexValid(height, index)) { |
| throw new ParseException("index out of bounds", 0); |
| } |
| position += indexSize; |
| secretKeySeed = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeySize); |
| position += secretKeySize; |
| secretKeyPRF = XMSSUtil.extractBytesAtOffset(privateKey, position, secretKeyPRFSize); |
| position += secretKeyPRFSize; |
| publicSeed = XMSSUtil.extractBytesAtOffset(privateKey, position, publicSeedSize); |
| position += publicSeedSize; |
| root = XMSSUtil.extractBytesAtOffset(privateKey, position, rootSize); |
| position += rootSize; |
| /* import BDS state */ |
| byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(privateKey, position, privateKey.length - position); |
| BDS bdsImport = (BDS) XMSSUtil.deserialize(bdsStateBinary); |
| bdsImport.setXMSS(builder.xmss); |
| bdsImport.validate(); |
| bdsState = bdsImport; |
| } else { |
| /* set */ |
| index = builder.index; |
| byte[] tmpSecretKeySeed = builder.secretKeySeed; |
| if (tmpSecretKeySeed != null) { |
| if (tmpSecretKeySeed.length != n) { |
| throw new IllegalArgumentException("size of secretKeySeed needs to be equal size of digest"); |
| } |
| secretKeySeed = tmpSecretKeySeed; |
| } else { |
| secretKeySeed = new byte[n]; |
| } |
| byte[] tmpSecretKeyPRF = builder.secretKeyPRF; |
| if (tmpSecretKeyPRF != null) { |
| if (tmpSecretKeyPRF.length != n) { |
| throw new IllegalArgumentException("size of secretKeyPRF needs to be equal size of digest"); |
| } |
| secretKeyPRF = tmpSecretKeyPRF; |
| } else { |
| secretKeyPRF = new byte[n]; |
| } |
| byte[] tmpPublicSeed = builder.publicSeed; |
| if (tmpPublicSeed != null) { |
| if (tmpPublicSeed.length != n) { |
| throw new IllegalArgumentException("size of publicSeed needs to be equal size of digest"); |
| } |
| publicSeed = tmpPublicSeed; |
| } else { |
| publicSeed = new byte[n]; |
| } |
| byte[] tmpRoot = builder.root; |
| if (tmpRoot != null) { |
| if (tmpRoot.length != n) { |
| throw new IllegalArgumentException("size of root needs to be equal size of digest"); |
| } |
| root = tmpRoot; |
| } else { |
| root = new byte[n]; |
| } |
| BDS tmpBDSState = builder.bdsState; |
| if (tmpBDSState != null) { |
| bdsState = tmpBDSState; |
| } else { |
| bdsState = new BDS(new XMSS(params)); |
| } |
| } |
| } |
| |
| public static class Builder { |
| |
| /* mandatory */ |
| private final XMSSParameters params; |
| /* optional */ |
| private int index = 0; |
| private byte[] secretKeySeed = null; |
| private byte[] secretKeyPRF = null; |
| private byte[] publicSeed = null; |
| private byte[] root = null; |
| private BDS bdsState = null; |
| private byte[] privateKey = null; |
| private XMSS xmss = null; |
| |
| public Builder(XMSSParameters params) { |
| super(); |
| this.params = params; |
| } |
| |
| public Builder withIndex(int val) { |
| index = val; |
| return this; |
| } |
| |
| public Builder withSecretKeySeed(byte[] val) { |
| secretKeySeed = XMSSUtil.cloneArray(val); |
| return this; |
| } |
| |
| public Builder withSecretKeyPRF(byte[] val) { |
| secretKeyPRF = XMSSUtil.cloneArray(val); |
| return this; |
| } |
| |
| public Builder withPublicSeed(byte[] val) { |
| publicSeed = XMSSUtil.cloneArray(val); |
| return this; |
| } |
| |
| public Builder withRoot(byte[] val) { |
| root = XMSSUtil.cloneArray(val); |
| return this; |
| } |
| |
| public Builder withBDSState(BDS valBDS) { |
| bdsState = valBDS; |
| return this; |
| } |
| |
| public Builder withPrivateKey(byte[] privateKeyVal, XMSS xmssVal) { |
| privateKey = XMSSUtil.cloneArray(privateKeyVal); |
| xmss = xmssVal; |
| return this; |
| } |
| |
| public XMSSPrivateKeyParameters build() throws ParseException, ClassNotFoundException, IOException { |
| return new XMSSPrivateKeyParameters(this); |
| } |
| } |
| |
| public byte[] toByteArray() { |
| /* index || secretKeySeed || secretKeyPRF || publicSeed || root */ |
| int n = params.getDigestSize(); |
| int indexSize = 4; |
| int secretKeySize = n; |
| int secretKeyPRFSize = n; |
| int publicSeedSize = n; |
| int rootSize = n; |
| int totalSize = indexSize + secretKeySize + secretKeyPRFSize + publicSeedSize + rootSize; |
| byte[] out = new byte[totalSize]; |
| int position = 0; |
| /* copy index */ |
| XMSSUtil.intToBytesBigEndianOffset(out, index, position); |
| position += indexSize; |
| /* copy secretKeySeed */ |
| XMSSUtil.copyBytesAtOffset(out, secretKeySeed, position); |
| position += secretKeySize; |
| /* copy secretKeyPRF */ |
| XMSSUtil.copyBytesAtOffset(out, secretKeyPRF, position); |
| position += secretKeyPRFSize; |
| /* copy publicSeed */ |
| XMSSUtil.copyBytesAtOffset(out, publicSeed, position); |
| position += publicSeedSize; |
| /* copy root */ |
| XMSSUtil.copyBytesAtOffset(out, root, position); |
| /* concatenate bdsState */ |
| byte[] bdsStateOut = null; |
| try { |
| bdsStateOut = XMSSUtil.serialize(bdsState); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| throw new RuntimeException("error serializing bds state"); |
| } |
| return XMSSUtil.concat(out, bdsStateOut); |
| } |
| |
| public int getIndex() { |
| return index; |
| } |
| |
| public byte[] getSecretKeySeed() { |
| return XMSSUtil.cloneArray(secretKeySeed); |
| } |
| |
| public byte[] getSecretKeyPRF() { |
| return XMSSUtil.cloneArray(secretKeyPRF); |
| } |
| |
| public byte[] getPublicSeed() { |
| return XMSSUtil.cloneArray(publicSeed); |
| } |
| |
| public byte[] getRoot() { |
| return XMSSUtil.cloneArray(root); |
| } |
| |
| public BDS getBDSState() { |
| return bdsState; |
| } |
| } |