package org.bouncycastle.pqc.crypto.xmss;

import java.util.ArrayList;
import java.util.List;

import org.bouncycastle.util.Arrays;

XMSS^MT Signature.
/** * XMSS^MT Signature. */
public final class XMSSMTSignature implements XMSSStoreableObjectInterface { private final XMSSMTParameters params; private final long index; private final byte[] random; private final List<XMSSReducedSignature> reducedSignatures; private XMSSMTSignature(Builder builder) { super(); params = builder.params; if (params == null) { throw new NullPointerException("params == null"); } int n = params.getDigestSize(); byte[] signature = builder.signature; if (signature != null) { /* import */ int len = params.getWOTSPlus().getParams().getLen(); int indexSize = (int)Math.ceil(params.getHeight() / (double)8); int randomSize = n; int reducedSignatureSizeSingle = ((params.getHeight() / params.getLayers()) + len) * n; int reducedSignaturesSizeTotal = reducedSignatureSizeSingle * params.getLayers(); int totalSize = indexSize + randomSize + reducedSignaturesSizeTotal; if (signature.length != totalSize) { throw new IllegalArgumentException("signature has wrong size"); } int position = 0; index = XMSSUtil.bytesToXBigEndian(signature, position, indexSize); if (!XMSSUtil.isIndexValid(params.getHeight(), index)) { throw new IllegalArgumentException("index out of bounds"); } position += indexSize; random = XMSSUtil.extractBytesAtOffset(signature, position, randomSize); position += randomSize; reducedSignatures = new ArrayList<XMSSReducedSignature>(); while (position < signature.length) { XMSSReducedSignature xmssSig = new XMSSReducedSignature.Builder(params.getXMSSParameters()) .withReducedSignature(XMSSUtil.extractBytesAtOffset(signature, position, reducedSignatureSizeSingle)) .build(); reducedSignatures.add(xmssSig); position += reducedSignatureSizeSingle; } } else { /* set */ index = builder.index; byte[] tmpRandom = builder.random; if (tmpRandom != null) { if (tmpRandom.length != n) { throw new IllegalArgumentException("size of random needs to be equal to size of digest"); } random = tmpRandom; } else { random = new byte[n]; } List<XMSSReducedSignature> tmpReducedSignatures = builder.reducedSignatures; if (tmpReducedSignatures != null) { reducedSignatures = tmpReducedSignatures; } else { reducedSignatures = new ArrayList<XMSSReducedSignature>(); } } } public static class Builder { /* mandatory */ private final XMSSMTParameters params; /* optional */ private long index = 0L; private byte[] random = null; private List<XMSSReducedSignature> reducedSignatures = null; private byte[] signature = null; public Builder(XMSSMTParameters params) { super(); this.params = params; } public Builder withIndex(long val) { index = val; return this; } public Builder withRandom(byte[] val) { random = XMSSUtil.cloneArray(val); return this; } public Builder withReducedSignatures(List<XMSSReducedSignature> val) { reducedSignatures = val; return this; } public Builder withSignature(byte[] val) { signature = Arrays.clone(val); return this; } public XMSSMTSignature build() { return new XMSSMTSignature(this); } } public byte[] toByteArray() { /* index || random || reduced signatures */ int n = params.getDigestSize(); int len = params.getWOTSPlus().getParams().getLen(); int indexSize = (int)Math.ceil(params.getHeight() / (double)8); int randomSize = n; int reducedSignatureSizeSingle = ((params.getHeight() / params.getLayers()) + len) * n; int reducedSignaturesSizeTotal = reducedSignatureSizeSingle * params.getLayers(); int totalSize = indexSize + randomSize + reducedSignaturesSizeTotal; byte[] out = new byte[totalSize]; int position = 0; /* copy index */ byte[] indexBytes = XMSSUtil.toBytesBigEndian(index, indexSize); XMSSUtil.copyBytesAtOffset(out, indexBytes, position); position += indexSize; /* copy random */ XMSSUtil.copyBytesAtOffset(out, random, position); position += randomSize; /* copy reduced signatures */ for (XMSSReducedSignature reducedSignature : reducedSignatures) { byte[] signature = reducedSignature.toByteArray(); XMSSUtil.copyBytesAtOffset(out, signature, position); position += reducedSignatureSizeSingle; } return out; } public long getIndex() { return index; } public byte[] getRandom() { return XMSSUtil.cloneArray(random); } public List<XMSSReducedSignature> getReducedSignatures() { return reducedSignatures; } }