1
I Use This!
Very High Activity

Commits : Listings

Analyzed about 13 hours ago. based on code collected about 14 hours ago.
Oct 02, 2025 — Oct 02, 2026
Commit Message Contributor Files Modified Lines Added Lines Removed Code Location Date
Adopt the state map withOwnedBDSState is handed, the way the single-tree sibling's overload does, rather than testing a maximum index that cannot differ. The method read maxIndexFor(val) and copied the map when the answer disagreed with the map's own - a defence against the legacy encoding that marks its maximum index with a negative value - but its one caller is extractKeyShard, six lines below, which hands over new BDSStateMap(this.bdsState, getIndex() + usageCount - 1): the copy constructor sets the maximum index to that argument, getIndex() is not negative and validateShardSize has already refused a usageCount below 1, so the two were always equal and the allocating branch never ran. Probed rather than reasoned about: with the branch replaced by a throw the four suites stay green, and with the whole method replaced by one XMSSMTTest reports five errors, so it is on the path and the branch is not. XMSSPrivateKeyParameters.Builder.withOwnedBDSState is the bare assignment this now is, and the resolution the public setter does for a legacy map stays where it was, said in the javadoc so its absence here is deliberate rather than missing. The package's AllTests at 102, AllTestsXMSS at 79 including the byte-identity oracle, the provider's XMSSTest at 33 and XMSSMTTest at 29, and checkstyleMain on core. More... 27 days ago
Keep one copy of the attributes fixture the twin provider tests round-trip. ATTRIBUTES - a PKCS#9 friendlyName in a DERSet - and the withAttributes / attributesOf pair that put it on a PKCS#8 encoding and read it back were declared in XMSSTest and again in XMSSMTTest, and all three are written in ASN1Set, PrivateKeyInfo and byte arrays with nothing in them that knows which of the two schemes it is serving. They join the other two shared helpers in XMSSTestUtils, and five imports each test no longer needs go with them. The cases keep their own bodies: what testAttributesSurviveSigningAndSharding asserts is that the attributes are still there after an encode, a signature and an extractKeyShard(1) on the key type that test is about, and that a generated key with no origin to take them from invents none. The provider's XMSSTest at 33 and XMSSMTTest at 29, and the core suites at 102 and 79. More... 27 days ago
Move the deadlock probe's thread into XMSSTestUtils, the second helper the twin provider tests each held a copy of. comparing(one, two, agreed, slot) is written entirely in java.security.PrivateKey and asks nothing of the key beyond equals(), so the twenty-five lines in XMSSTest and the twenty-five in XMSSMTTest were the same lines twice; what differs between the two testEqualsTakesTheTwoKeyMonitorsOneAtATime cases is the parameter set the keys come from, and that stays where it is. The javadoc it gains says what the thread is for - two of them started against the same pair in opposite orders, where nested monitors stop both within a few rounds and neither reaches the write - which the case's own javadoc explains from the other end. The provider's XMSSTest at 33 and XMSSMTTest at 29, and the core suites at 102 and 79. More... 27 days ago
Put hasLegacyBdsMarker in one place, XMSSTestUtils, rather than a copy in each of the twin provider tests. XMSSTest and XMSSMTTest carried the same twenty lines - a substring search for the class name "org.bouncycastle.pqc.crypto.xmss.BDS" that the Java-serialized state of a pre-promotion key writes in the clear - with nothing in either copy naming the other, and the search has no XMSS-versus-XMSS^MT typing in it at all: it takes a byte array and answers about bytes. The new class sits beside PQCSigUtils, which is where this package already keeps a helper two tests share, and its javadoc says what the marker is and why a fixture that has lost it is no longer the encoding the test means to read. The provider's XMSSTest at 33 and XMSSMTTest at 29, and the core suites at 102 and 79. More... 27 days ago
Take the leading index off an encoding with Pack.bigEndianToLong_Low, which OneTimeKeyReuseTests in this package already calls for exactly this and which SignerConcurrencyTests had a private loop of its own for. The helper it replaces was the same shift-and-or over the first indexSize bytes, right-aligned into a long, and it stood at the bottom of the file where its javadoc was the only place saying what those bytes are; that sentence moves to the two sites that read a signature, next to the RFC section fixing the width - four bytes for XMSS, ceil(h / 8) for XMSS^MT - which is where the sibling test keeps it. The four call sites read the same widths from the same offsets as before. The package's AllTests at 102, AllTestsXMSS at 79 including the byte-identity oracle, the provider's XMSSTest at 33 and XMSSMTTest at 29. More... 27 days ago
Read the four magic bytes with Pack.bigEndianToInt rather than four masked comparisons of its own. hasMagic asked the question a byte at a time - shift the constant down, mask, compare, and again - which is the big-endian int decode org.bouncycastle.util.Pack exists to hold, and which the rest of this codec already goes through DataInputStream for. The length guard stays where it was, ahead of the read, because Pack does not carry one. The package's AllTests at 102, AllTestsXMSS at 79 including the byte-identity oracle, the provider's XMSSTest at 33 and XMSSMTTest at 29. More... 27 days ago
Compare an XMSS^MT public key through getEncoded(), the way the single-tree sibling already does, rather than through the toByteArray() that class deprecates. XMSSMTPublicKeyParameters.toByteArray() carries "@deprecated use getEncoded() - this method will become private", and getEncoded() is a one-line delegation to it, so the bytes equals() and hashCode() see do not move - what changes is that this key stops being one of the callers standing between that method and the private it is headed for. BCXMSSPublicKey compares the same way for the same reason and has to catch the IOException getEncoded() declares but cannot throw on this path; the catch is copied across with its comment so the two read alike. The provider's XMSSMTTest at 29. More... 27 days ago
Open org.bouncycastle.pqc.jcajce.provider.xmss to java.base, which every provider package the BC provider instantiates from is opened for and this one was not. The package has been exported since it was written, which is what a BCPQC-only package needs, and the line beside it - opens org.bouncycastle.pqc.jcajce.provider.lms to java.base - was added by b52380c27a ("added export to java.base for lms algorithm") at the moment LMS was wired into BouncyCastleProvider's ASYMMETRIC_CIPHERS, which is the step this branch takes for XMSS. Every package listed in that opens block is one the provider names by string and reaches reflectively, and the block is where the newer algorithms all sit; xmss is now in the same position and was the one entry missing. Both descriptors get it, the Gradle-driven jdk1.9 one and the ext-jdk1.9 one the legacy distribution consumes, since those are kept in step by hand. Nothing in this tree exercises it: no test here runs the provider on the module path, so the descriptor is checked by compilation and by the sibling it now matches rather than by a run. More... 27 days ago
Report the usages a signer's key has left without taking the signer's monitor, which none of the implementations this sits beside takes for it. getUsagesRemaining() reads the private key field and asks the key, and what comes back is a count rather than a reservation: it is out of date the moment a monitor held over it is dropped, because the next signature made on that key - by this signer, or by another one initialised on it - moves it. So a lock here buys the caller a number no more current than the unlocked read gives, at the price of contending with the signature that is making it stale. The field read is of a reference, which cannot tear, and the key's own getUsagesRemaining() takes the key's monitor for the traversal state the count is derived from, which is the read that has two fields to keep together. This is where org.bouncycastle.pqc.crypto.xmss leaves it - neither XMSSSigner.getUsagesRemaining nor the XMSS^MT one is synchronized there - and where the promoted LMS leaves it, LMSPrivateKeyParameters and HSSPrivateKeyParameters both carrying an unsynchronized getUsagesRemaining beside the synchronized getIndex the same class defines. What stays on this signer's monitor is init(), generateSignature() and getUpdatedPrivateKey(), the three that write the private key field or spend the one-time key it names; five sites each, down from seven. The package's AllTests at 102, AllTestsXMSS at 79 including the byte-identity oracle, the provider's XMSSTest at 33 and XMSSMTTest at 29, and checkstyleMain on core. More... 27 days ago
Verify without taking the signer's monitor, the way the XMSS signers this promotes and LMSSigner both verify. verifySignature() reads the public key and the mode flag, spends no one-time key and advances no traversal state, so there is nothing in it for a monitor to serialize - and the monitor could not serialize the one thing that looks like it needs it, because update() and reset() write the message buffer without taking it, so a lock held over the read alone excludes nothing a caller sharing one signer between threads is already doing. What the signing side takes this monitor for is the private key field, which getUpdatedPrivateKey() reassigns and init() reassigns: that is a field the verification side does not touch, and nothing writes the public key but init(), where a signer re-initialised under a running operation is the caller error it is in every other signer here. So the signing side keeps both monitors and the verification side takes neither, which is also where the two implementations being promoted from left it - org.bouncycastle.pqc.crypto.xmss's XMSSSigner and XMSSMTSigner synchronize on the private key in generateSignature and nowhere near verifySignature - and where the sibling scheme leaves it, LMSSigner and HSSSigner and LMSEngine carrying no synchronized at all between them and every lock LMS takes living inside the key parameters. The reason for the absence goes into the javadoc rather than being left to be rediscovered. The package's AllTests at 102, AllTestsXMSS at 79 including the byte-identity oracle, the provider's XMSSTest at 33 and XMSSMTTest at 29, and checkstyleMain on core. More... 27 days ago
Drop the AddressTests case asserting the three address types are distinct, which the three layout tests beside it already decide. Each of those asserts the type word of the encoding its own type produces - 0x00 for the OTS hash address the factory builds, 0x01 and 0x02 for the two subtreeAddressOf derives from it - and all three build from the same layer and tree, so the moment those three assertions hold the three encodings differ in bytes 12 to 15 and no pair of them can compare equal. The case had no mutation of its own to catch: a build whose subtreeAddressOf leaves the type word where the fresh array leaves it, which is what makes all three encodings identical and is the one thing this case existed to notice, fails testLTreeAddressLayout and testHashTreeAddressLayout at expected 1 and 2 against 0, so it is refused two tests before reaching this one. The package's AllTests at 102, down from the 103 it ran with the case in. More... 27 days ago
Let DigestUtil refuse a null tree digest OID rather than checking for one first. The check stood at the top of the two-argument WOTSPlusParameters constructor and nothing in this tree could reach it: XMSSParameters and XMSSMTParameters throw their own NullPointerException("digest == null") before they get as far as XMSSEngine.getWOTSPlusLen, and XmssKeyUtil takes the OID off an AlgorithmIdentifier and puts it through DigestUtil.getDigest before anything reaches BDS.withWOTSDigest. It did not cover its own class either - the one-argument constructor evaluates DigestUtil.getDigest(treeDigest).getDigestSize() in the this(...) argument list, so a null dies inside getDigest before the constructor holding the check is entered, and withWOTSDigest(oid) and withWOTSDigest(oid, size), two overloads of one public method on an exported package, answered the same mistake two different ways. Removing it lets nothing through, because DigestUtil is already what refuses the null on that other path: getDigestName looks the OID up with a HashMap.get, which takes a null key, and reports it as IllegalArgumentException("unrecognized digest oid: null") - the wording it gives every OID it does not know. What that costs is the exception type on the three ways in that did reach the check, and the two constructors still differ from each other, because which of getDigest and getDigestName lies on the path is what decides it rather than a check of ours. The OID lookup below stays, and is not this: it is the only place a security parameter no XMSS parameter set defines is refused, and with it shadowed out new XMSSParameters(4, id_sha256, n) at n = 17, 20, 33 and 64 all generate a key pair, sign and verify with parameterSetOID 0, every node above the digest's own 32 bytes carrying a constant zero tail - the measured n = 64 root ends in 32 of them. ParameterBoundsTests asserts its message across nine sizes and both schemes. More... 27 days ago
Make an OTS hash address with a static factory on XMSSAddress, and delete OTSHashAddress. It was the last address object - a class holding one int, whose every use was to make one and encode it in the same expression - where an L-tree address and a hash tree address had already become static byte[] on XMSSAddress when the walks stopped holding addresses and started stepping encodings. So the one 32-byte frame RFC 8391 sec. 2.5 defines was laid out across two files, and the split ran through the middle of a single walk: WOTSPlus.getWOTSPlusSecretKey clears the chain address, the hash address and the key-and-mask of the encoding it was handed on three consecutive lines, and had to name the first two out of OTSHashAddress and the third out of XMSSAddress. otsHashAddress(layerAddress, treeAddress, otsAddress) takes what the constructor took and writes the same four words in the same order, the type among them: that word is zero and a fresh array holds zero, but the other two types are written and dropping this one would be a change of its own rather than part of this. The three offsets and otsAddressOf move across, each landing beside the name another type gives the same word - OTS_ADDRESS_OFFSET next to LTREE_ADDRESS_OFFSET, CHAIN_ADDRESS_OFFSET and HASH_ADDRESS_OFFSET next to TREE_HEIGHT_OFFSET and TREE_INDEX_OFFSET - which is the aliasing the RFC has and which a reader could not see while it was split. Two names for one offset here are not a constant written twice, the way the tree height and the tree index were before they were merged: they are what the type word decides between, and each says at its use site which walk is stepping. More... 27 days ago
Build an OTS hash address with a constructor rather than a builder, and delete the two builder classes. All eighteen places that made one made it in a single expression and encoded it there and then - new OTSHashAddress.Builder()...build().toByteArray() - so nothing in the package ever held an address, and the two builders existed to let five call shapes leave out the words they do not set. A constructor taking the three those shapes vary, the layer and the tree and the leaf, fits each on one line and says which word every argument goes to, where a chain of setters had to be read to the end to find out. That takes XMSSAddress.Builder<T extends Builder> with its self type and its abstract build() and getThis(), and OTSHashAddress.Builder with it. The key-and-mask goes as well, field and all. It is the one word production never set on an address - the only caller of withKeyAndMask in the tree was this package's own address test - because it varies per hash rather than per address: WOTSPlus.chain writes it into the encoding it was handed twice per chain step, XMSSNodeUtil.randomizeHash three times per node, and getWOTSPlusSecretKey puts it back to zero before each one-time key. toByteArray() was writing a zero into a word of an array Java had just zeroed, so not writing it changes nothing; that a fresh encoding leaves it zero is now something AddressTests asserts rather than something the write made obvious, and the test sets its own through KEY_AND_MASK_OFFSET the way it already set the chain address and hash address words. The encodings are unchanged and shown to be at both levels this touches. XMSSPromotionCompatibilityTest compares signatures byte for byte against the legacy implementation, which still builds its addresses through builders, and it discriminates: a probe making the constructor drop the OTS address word fails it, and a probe dropping the layer argument at the two XMSS^MT call sites fails three of its six. A third probe restoring a non-zero key-and-mask write passes that oracle and fails AddressTests, which is the measurement that the dropped write was unobservable in production and that the new assertions are what hold it now. The package's AllTests at 103, the oracle at 6, AllTestsXMSS at 79, pqc.crypto.test.AllTests at 136, the provider's XMSSTest at 33 and XMSSMTTest at 29, and checkstyleMain on core and prov. More... 27 days ago
Name the words of the two address types nothing builds in the class that lays their frame out, and delete the classes. RFC 8391 sec. 2.5 gives every address type one 32-byte frame and numbers its words the same way, so an L-tree address and a hash tree address put the tree height and the tree index at the same two words - and with both types reduced to a bag of constants when the walks stopped building addresses and started stepping encodings, each of those two words was named twice, once per class, with a sentence in each class's javadoc pointing at the other to say the two agreed. That is one layout written down in two places with nothing to keep them in step, and neither class had anything else left: both opened by saying nothing builds one. Their type words and offsets go to XMSSAddress, beside TYPE_OFFSET and KEY_AND_MASK_OFFSET and the subtreeAddressOf that is the only thing that makes either type, so the tree height and the tree index are named once rather than once per type. The two type words become LTREE_TYPE and HASH_TREE_TYPE, one class having no room for two called TYPE. OTSHashAddress keeps its own three offsets: it is the one type still built, so it is the one that lays out its own words. Behaviour is unchanged and shown to be. The offsets are compile-time constants, inlined at every use, so the four classes that read them - BDS, BDSTreeHash, XMSSNodeUtil and XMSSVerifierUtil - disassemble identically before and after but for constant pool numbering, the two deleted class entries being what left the pool; a probe moving TREE_INDEX_OFFSET from 24 to 20 shows up in that same comparison as bipush 24 becoming bipush 20 at all three of BDSTreeHash's sites, so the comparison does discriminate. The package's AllTests at 103, XMSSPromotionCompatibilityTest at 6, AllTestsXMSS at 79, pqc.crypto.test.AllTests at 136, the provider's XMSSTest at 33 and XMSSMTTest at 29, and checkstyleMain on core and prov. More... 27 days ago
Resolve an XMSSParameterSpec tree digest name in one table, rather than in seven branches per key pair generator. Both SPIs carried the same seven, in the same order, mapping the same names to the same OID and security parameter, and the only thing that differed between the two copies was which parameter set class the branch went on to build - so a digest added to one and not the other leaves XMSS and XMSS^MT disagreeing about which names this provider accepts, and there is nothing that would say so. More... 27 days ago
Resolve a public key's three fields in the class that holds the layout they are stored in, the way the private keys already do. Both public key classes decided the same thing the same way: an encoding, if the builder was given one, taken apart by XMSSPublicKeyCodec; otherwise the parameter set's own identifier and the root and seed checked against n. The codec had the encoding half and the two constructors had that decision, word for word, so a rule about how a public key resolves its OID, root and seed - and the identifier is exactly where the two families differ, which is what makes it look like a per family rule - had two places to be applied to and no compiler check that both were. More... 27 days ago
Refuse a key shard the key cannot give in one place for both families, and assert the two messages that say which refusal it was. extractKeyShard had the same pair of guards in both private key classes, the same two IllegalArgumentException strings written out twice, and a caller has nothing but those strings to tell "you asked for none" from "you asked for more than there is" - so the messages are the contract and were the thing kept in step by hand. They go where validateOrAllocate is, beside the other check the key classes share. More... 27 days ago
Read a reduced signature out of the encoding that carries it, at the offset it sits at, rather than out of a slice of a copy of it. encodeTo() writes signature || authentication path into the caller's buffer at a position, because the encodings that carry one are filling a buffer of their own - XMSSSignature puts index || random in front of it, XMSSMTSignature lays one down per layer. The read side had no such form: both callers cut the region out with copyOfRange and handed it to a builder that cloned it again, so the payload - len n-byte WOTS+ blocks and h authentication path nodes - was copied twice before the constructor copied it a third time into the blocks and nodes it keeps. Only the third copy is a copy of anything the built object holds; the first two were of a region read once and dropped. For XMSS^MT the first of them was itself a slice of the clone withSignature() had already taken. More... 27 days ago
Take over the collections a decoder built rather than copying them out from under it. The field by field BDS constructor cloned all five - the authentication path, the retain map and every queue in it, the stack, a clone of each tree hash instance, and the keep map - and its production caller is BDSStateCodec.readBDS, which assembles each of them a node at a time out of the stream it is reading and holds the only reference to what it hands over. So every private key decode built the traversal state twice and dropped the first one: 950 of the 8113 bytes an h = 10 SHA-256 key decode allocates, and 1712 of the 14814 an h = 20 d = 2 XMSS^MT key does, both measured JIT warm over 20000 decodes with the decoded root printed alongside so the two builds are visibly decoding to the same key. More... 27 days ago
Import a placeholder where advancing a BDS state needs the public seed and not the one-time key. The branch nextAuthenticationPath takes when the leaf is a right node hands randomizeHash the node the state kept and the one the path already holds, and randomizeHash reads getPublicSeed() and getKhf() off the WOTS+ instance and nothing else - so the importKeys ahead of it was there for the seed, and the getWOTSPlusSecretKey inside it was a PRF whose result nothing read. The left node branch above it does need the key, because it derives a WOTS+ public key from it. More... 27 days ago
Read a public key's root once per verification rather than at each of the two places a verification wants it. getRoot() hands out a clone - the field is the key's, and a caller that wrote to what it got back would change what every later verification compares against - so asking twice made two n-byte copies of a field that is written at construction and never after. Both verifiers asked twice, for the middle of the H_msg key and then for the comparison at the end, and the signing side beside them already reads its key's root once. More... 27 days ago
Ask hasTraversalState whether a key can sign where the signature methods asked the state directly. The two are the same question and one of them is a public helper with a javadoc saying so, which the signers call before they reach the engine; the engine's own copy of the check reached past it into the state - isAuthenticationPathEmpty() on the lone BDS, isEmpty() on the state map - so what counts as no state was written down in three places for two families. Widening it, and the reason to widen it is that the two families already answer differently, would have meant finding the two private call sites that do not go through the helper their own class declares. More... 27 days ago
Drop the two OTS hash address setters no walk calls, and have the address test write those words the way the walks write them. withChainAddress and withHashAddress had no caller in the tree outside AddressTests: every signing and verification site builds the address with withOTSAddress alone and then steps the chain and hash address words through the encoding it holds, WOTSPlus.chain writing both once per chain step at CHAIN_ADDRESS_OFFSET and HASH_ADDRESS_OFFSET, and getWOTSPlusSecretKey putting them back to zero before each one-time key. So the fields behind the setters were zero in every address this package builds, and toByteArray() wrote two zeros into two bytes of a fresh array that already held them; a reader had to walk every call site to find that out. More... 27 days ago
Ask org.bouncycastle.util.Arrays whether a byte[][] holds a null, rather than walking it here. hasNullPointer was a byte for byte carryover from the deprecated pqc.crypto.xmss copy, and isNullOrContainsNull(Object[]) is the same walk with the same answer for the same argument - a byte[][] is an Object[] - in the class this file already imports for the row comparison the caller goes on to make. The same commit that promoted this file, 9180cbfec6, replaced the method beside it, log2, for exactly this reason and left this one; areEqual is its only caller and there is nowhere else in the package a second one could come from, XMSSUtil being package private. More... 27 days ago
Record that a signer has spent its key from what the signature did to the key, not from having called the engine. hasGenerated is what getUpdatedPrivateKey() reads to decide between handing the key straight back - the engine having already rolled it - and advancing it first, and both signers set it on the line before the engine call. Neither side of that call is the answer. The engine refuses a signature before it touches the key: its "one time key at index N has already signed" check sits ahead of the try whose finally rolls, and it is the one guard the signer does not repeat, so it is reachable with the flag already set. It also rolls the key from that finally once it has started, so a signature that fails part way through leaves a rolled key, and setting the flag after the call would leave that one looking unspent. More... 27 days ago
Compare an XMSS^MT key's two records of its position where it spends a one-time key, not only where it is moved and stored. The key records where it has got to twice - the index field, and the per-layer BDS traversal states - and github #2414 closed that by comparing them wherever the pair could have come apart: the constructor and builder do it, rollKey() does it, and both encoders do it. Signing did not. What it asked instead was isUsed(), which is the other half of the question - whether the layer zero state has already signed where it stands, not whether it is standing where the index says it is - and a state that lags without having signed there answers no to it. More... 27 days ago
Read a state map's top layer under that map's monitor, as every other read of it is. validateRoot() was the one accessor on BDSStateMap that reached bdsState with no synchronized block around it - isEmpty(), getStateMap(), validate(), validateIndex(), get(), put() and update() all take the monitor, and the hardening that gave them one left this one behind. It is not an accessor that runs at a quiet moment: the XMSS^MT key constructor and its builder both call it, and the state map a key is being built around is reachable from a live key through getBDSState(), so the map being read can be one a signature is descending. That signature puts each lazily built layer into the same map as it goes - only the top layer exists at key generation - and an insertion rebalances the TreeMap under the lookup. More... 27 days ago
Say what getEncodedBDSState(BDS) writes, rather than describing the form it reads. Its javadoc called the encoding it produces "the legacy Java-serialized form" and said "nothing generates it any more" - of the one method in this tree that generates it. Both halves of that belong to deserialize(): the legacy names CheckingStream resolves are what nothing writes any more, and the comment there says so correctly, that serialize() has emitted the versioned BDSStateCodec form since the codec was introduced. Carried onto the encoder, it tells a reader that a live key's toByteArray() and the state half of its equals() - which is what reaches this, along with XmssKeyUtil writing a PKCS#8 - go through a path kept only for old keys, so a change made here looks like it can only affect reading them. More... 27 days ago
Compare and hash an XMSS private key in the class that holds the layout it is stored in, rather than writing the same method out once per family. XMSSPrivateKeyParameters and XMSSMTPrivateKeyParameters carried a line for line copy of one equals() and one hashCode() each - the same seven fields in the same order, the same chain joined the same way, the same two monitors taken in the same sequence, and the same hundred lines of javadoc saying why - which is the shape their encodings were in before XMSSPrivateKeyCodec held those. So the comparison goes where the layout is: what a key is compared on is what a key is stored as, the index and the four n-byte fields, and the codec is package private in the same package as both, so nothing here is new public API and the crypto/params/XMSS* excludes the jdk1.4 and jdk1.3 Ant builds carry keep covering it. More... 28 days ago