public class BDSStateMap
extends java.lang.Object
implements java.io.Serializable
Every read and every write of the map below is taken on this object's own monitor, and one fact
is behind all of them: XMSSMTPrivateKeyParameters.getBDSState() hands this object out
live, and a signature descends the layers installing the states it builds lazily into the map it
is signing with. An insertion rebalances the TreeMap underneath, so anything walking or
looking up outside the monitor reads a tree part way through being restructured - a
ConcurrentModificationException at best, and at worst a lookup that answers with a null
or with another layer's state. Holding the monitor for the whole of an operation, rather than
once per lookup, is also what makes an answer coherent rather than merely intact: the signer
holds this same monitor for its whole descent, so what an operation here sees is every layer
from before that signature or every layer from after it, never a mixture.
What each method does with that is on the method. None of them repeats this.
| Constructor and Description |
|---|
BDSStateMap(BDSStateMap stateMap,
long maxIndex)
Copy the states of another map, on that map's own monitor for the whole walk - which is what
makes this safe to run against a map a key is signing with, for the reason on the class.
|
BDSStateMap(long maxIndex) |
BDSStateMap(XMSSMTParameters params,
long globalIndex,
byte[] publicSeed,
byte[] secretKeySeed) |
| Modifier and Type | Method and Description |
|---|---|
BDS |
get(int index) |
long |
getMaxIndex() |
void |
validate(XMSSMTParameters params) |
void |
validate(XMSSMTParameters params,
long globalIndex)
Validate as validate(XMSSMTParameters) and additionally tie each layer's traversal state to
the enclosing private key's index.
|
void |
validateIndex(XMSSMTParameters params,
long globalIndex)
The index half of validate(XMSSMTParameters, long) on its own: tie each layer's traversal
state to the index its enclosing key declares, without re-walking the structure of every
state.
|
void |
validateRoot(XMSSMTParameters params,
byte[] expectedRoot)
Confirm the top layer's root is the one the enclosing private key declares - the top tree's
root is the public root.
|
BDSStateMap |
withMaxIndex(long maxIndex,
ASN1ObjectIdentifier digestName,
int digestSize)
As
withWOTSDigest(ASN1ObjectIdentifier, int), and with a maximum index of the
caller's rather than this map's - the state map counterpart of
BDS.withMaxIndex(int, ASN1ObjectIdentifier, int), and what
XMSSMTPrivateKeyParameters.Builder.withBDSState copies a state map with, so that a
state installed in a key is always one whose WOTS+ parameters the key's own parameter set
named. |
BDSStateMap |
withWOTSDigest(ASN1ObjectIdentifier digestName) |
BDSStateMap |
withWOTSDigest(ASN1ObjectIdentifier digestName,
int digestSize) |
public BDSStateMap(long maxIndex)
public BDSStateMap(BDSStateMap stateMap, long maxIndex)
Each layer is copied rather than shared, and the copy is not incidental. A BDS is
filled at construction and never written afterwards - the block above BDS's getLive
accessors says so, and advancing a layer builds its successor and puts it here - with
one exception: markUsed() writes the used mark in place. This constructor has two
callers, getNextState(org.bouncycastle.crypto.params.XMSSMTParameters, long, byte[], byte[]) and XMSSMTPrivateKeyParameters.extractKeyShard, and it
is the second that leaves two live keys holding maps built from one map. A shared layer zero
would be those two keys sharing one record of whether the one-time key at that leaf has been
spent - the mark one of them made becoming the refusal the other gets, and the mark it did
not make becoming the refusal it does not get, which is the direction RFC 8391 sec. 1.1 is
about. Only layer zero is ever marked, so sharing the layers above it would be safe as
markUsed() is written today; that is a fact about that method rather than about this
one, and it would buy the copy on getNextState's roll path alone.
The two key builders' withBDSState leave two live keys the same way and reach the
same guarantee, but not through here: they go through withMaxIndex(long, org.bouncycastle.asn1.ASN1ObjectIdentifier, int), which builds an
empty map and fills it a layer at a time out of BDS.withWOTSDigest(org.bouncycastle.asn1.ASN1ObjectIdentifier), itself a copy.
So there are two ways a state map is copied per layer in this package rather than one, and a
change to the copying here covers neither builder.
public BDSStateMap(XMSSMTParameters params, long globalIndex, byte[] publicSeed, byte[] secretKeySeed)
public long getMaxIndex()
public void validate(XMSSMTParameters params)
public void validateRoot(XMSSMTParameters params, byte[] expectedRoot)
params - the parameters of the enclosing key.expectedRoot - the root the private key declares.public void validate(XMSSMTParameters params, long globalIndex)
params - the parameters of the enclosing key.globalIndex - the index the enclosing key declares.public void validateIndex(XMSSMTParameters params, long globalIndex)
Separate because the two halves answer at different times. The structure of a state can only be wrong on the way in, so the constructor is where it is checked; the index pair can go wrong every time the key moves, because the two records are advanced by two statements and only the author of those statements keeps them together. So this half runs on every roll and before every encoding, where the full check would be re-walking authentication paths, stacks and tree hashes once per signature to learn nothing new.
params - the parameters of the enclosing key.globalIndex - the index the enclosing key declares.public BDS get(int index)
public BDSStateMap withWOTSDigest(ASN1ObjectIdentifier digestName)
public BDSStateMap withWOTSDigest(ASN1ObjectIdentifier digestName, int digestSize)
public BDSStateMap withMaxIndex(long maxIndex, ASN1ObjectIdentifier digestName, int digestSize)
withWOTSDigest(ASN1ObjectIdentifier, int), and with a maximum index of the
caller's rather than this map's - the state map counterpart of
BDS.withMaxIndex(int, ASN1ObjectIdentifier, int), and what
XMSSMTPrivateKeyParameters.Builder.withBDSState copies a state map with, so that a
state installed in a key is always one whose WOTS+ parameters the key's own parameter set
named. Only the map's own maximum index is the caller's: each layer keeps the one its
subtree fixes, as it does through every other copy of a state map.
The two used to have to be done one after the other, and in one order, because copying a state that had not been given its digest yet was a NullPointerException; they can be done in either order now, and this does them in one pass rather than either.
maxIndex - the maximum index the copy is to carry.digestName - the tree digest of the key the copy belongs to.digestSize - its output length in bytes, where the digest does not fix one.