add support for pkcs11 v3.2 ml-kem - #131
Draft
Nicolas-Peiffer wants to merge 69 commits into
Draft
Conversation
Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
this is temporary we wait on the PR to be merged
…s := opts.(type)) could eliminate type assertions in switch cases https://staticcheck.dev/docs/checks/#S1034
…tAccessSameLibraryTwice uses FindKeys; TestNoLogin cleans up unexpected key and skips; TestInvalidPinDoesntDestroyLibrary skips gracefully when tokens absent
…oken2" for TestInvalidPinDoesntDestroyLibrary
Algorithm support tables covering RSA, ECDSA, DSA, ML-KEM (all three security levels), AES, DES3, certificates, HMAC, and RNG — with which operations each supports ML-KEM noted as requiring PKCS#11 v3.2 and SoftHSMv3 New SoftHSMv3 testing section explaining the automated SOFTHSM3_MODULE workflow SoftHSMv2 section updated with a markdown link and a note that ML-KEM tests auto-skip on v2
aead.go (HIGH): copy the HSM-generated GCM IV back into the caller's nonce slice whenever UseGCMIVFromHSM is set. The previous code only length-checked it, so the caller never learned the actual IV — making the ciphertext undecryptable or, worse, enabling GCM nonce reuse. common.go (MEDIUM): fix out-of-bounds read in bytesToUlong. The prior unsafe dereference always read sizeof(CK_ULONG) bytes regardless of slice length, reading past the end of shorter attributes like a 4-byte CKA_PARAMETER_SET. Copy into a zero-padded [sizeof_ulong]byte first. crypto11.go (MEDIUM): - Zero object handles to CK_INVALID_HANDLE after a successful Delete so any subsequent use fails loudly instead of silently targeting a recycled handle on a different key. - Make Close idempotent with sync.Once; a second Close previously triggered a refcount panic inside moduleCtx.Close. - Key the module cache by absolute path before the lookup, not after, so two spellings of the same library share one C_Initialize call. - Wipe the transient PIN []byte with pkcs11.Wipe immediately after Login and clear cfg.Pin on the retained copy; take a defensive shallow copy of *Config at Configure entry so callers are never affected by our default-filling or PIN clearing. mlkem.go (LOW): bounds-check the GetAttributeValue result in Bytes() before indexing; zero pubKeyHandle after Delete; reject unknown paramSet values with a clear error before generating a key pair.
Nicolas-Peiffer
force-pushed
the
pkcs11-v3.2-ml-kem
branch
from
June 17, 2026 15:29
c23b625 to
c9a4a26
Compare
Add the shared CI standard used across pkcs11-go / crypto11 / gose: CodeQL, govulncheck, Gitleaks, OpenSSF Scorecard, dependency review, golangci-lint and grouped Dependabot, plus .golangci.yml. Resolve the local `replace => ../pkcs11-go` directive via the committed vendor/ tree (GOFLAGS=-mod=vendor), dropping the previous checkout-and- copy-sibling steps. CI keeps the SoftHSM2 test job. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…x URL Align with the shared env var convention across the four repos: - SOFTHSM3_MODULE → PKCS11_MODULE - SOFTHSM3_PIN → PKCS11_PIN Add a skip-all guard in TestMain: when neither PKCS11_MODULE nor crypto11.config.json is present (CI without a provisioned HSM), exit 0 instead of failing every test with CKR_GENERAL_ERROR. Drop the softhsm2-util pre-init from ci.yml — TestMain now bootstraps tokens entirely via the PKCS#11 API when PKCS11_MODULE is set, so no external tool is needed. ML-KEM and v3.2 tests self-skip on SoftHSM2. Fix SoftHSMv3 repository URL: pqctoday/softhsmv3 → pqctoday-org/pqctoday-hsm. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Separate the go directive (minimum consumer requirement, no patch pin) from the toolchain directive (maintainer build toolchain), following Go 1.21+ semantics. Closes #137. Remove the hand-written min(a, b int) helper — the Go 1.21 builtin covers this generically. Document the two-directive pattern in README.
Closed
makeKeyPair, makePrivateKey and makeRSAKeyPair return an error for any CKA_KEY_TYPE this package cannot represent, but the loops that call them only skipped errNoCkaID and errNoPublicHalf. Every other error aborted the whole walk, so a single object of an unknown type made FindAllKeyPairs, FindAllKeys, FindPrivateKeysWithAttributes, FindKeyRSAPairsWithAttributes and FindAllPairedCertificates fail outright with "unsupported key type: %X", hiding every other key on the token. v2 turns that six-year-old report into a regression we ship ourselves: an ML-KEM private key is a CKO_PRIVATE_KEY whose type none of those finders understand, so as soon as a user generates an ML-KEM key pair, enumerating the rest of that token stops working. Wrap the "unsupported key type" errors in a new errUnsupportedKeyType sentinel and skip it in the enumeration loops, next to errNoCkaID and errNoPublicHalf. FindKeysWithAttributes does the same for symmetric keys with no matching Cipher, and makeRSAKeyPair's "not an RSA key pair" wraps the sentinel too, so an RSA-only finder walking a mixed token no longer trips over the first EC key it meets. Singular lookups (FindKeyPair, FindKey, FindPrivateKey) now report "not found or is empty" instead of "unsupported key type" for such keys, which matches how they already behave when the public half is missing. Adds a regression test that puts an ML-KEM key pair and an RSA key pair on one token and checks all four enumerators still succeed. Reported by @Knacktus in #68 back in 2020, and independently run into by @droppingin, who cross-referenced it from #103 while fixing the Windows CK_ULONG conversions. Thank you both — the report was accurate the whole time, it just took us until the ML-KEM work to feel the pain ourselves. Fixes #68 Refs #103 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Requires pkcs11-go v1.1.0-rc1, which exports the CK_ULONG codec this package had been carrying a broken copy of. CK_ULONG is a C unsigned long: 8 bytes under LP64 (Linux, macOS), 4 under Windows' LLP64 model and on 32-bit targets. Go's uint is 8 bytes everywhere. bytesToUlong reinterpreted the address of its buffer as a Go uint, which always reads 8 bytes, so on Windows it read 4 bytes past a 4-byte CK_ULONG buffer and returned whatever sat behind it in the top half of the value. Every CK_ULONG attribute was affected — CKA_KEY_TYPE, CKA_MODULUS_BITS, CKA_VALUE_LEN — which is how this first surfaced, as FindAllKeyPairs failing under SoftHSM2 on Windows. The v2 security audit fixed the neighbouring out-of-bounds read on short attributes (a 4-byte CKA_PARAMETER_SET) by copying into a sizeof(CK_ULONG) buffer first, but kept the same width assumption when reading that buffer back, so on Windows the read only moved from overrunning the caller's slice to overrunning our own buffer. Rather than fix the copy here, delete it. Three changes, all of them moving C-ABI knowledge down into the one package that legitimately holds the PKCS#11 headers: 1. ulongToBytes / bytesToUlong are gone; call sites use pkcs11.ULongToBytes / pkcs11.BytesToULong, which size the conversion from the C type and carry the tests. common.go held the only import "C" in crypto11, so the package no longer contains any cgo of its own (it still builds with cgo, through the binding). 2. signPSS builds its parameter with NewMechanismWithParams and NewPSSParams instead of concatenating three CK_ULONGs by hand, so the binding marshals the real CK_RSA_PKCS_PSS_PARAMS struct. This is what the "TODO this is pretty horrible, maybe the PKCS#11 wrapper could be improved to help us out here" comment was waiting for; OAEP and GCM parameters were already built this way. concat had no other caller and is removed with it. 3. FindRSAPrivateKeysWithAttributes gets the same skip the other finders got in f2bc2f6 — it walks every CKO_PRIVATE_KEY on the token, so one EC or ML-KEM key used to abort the whole enumeration. makeRSAPrivateKey's error for a non-RSA key also wrapped a nil err, rendering as "not an RSA key type: %!w(<nil>)" and matching no sentinel at all; it now reports the key type it found and wraps errUnsupportedKeyType. Reported by @droppingin in #103, with the analysis, the C data-model references and a SoftHSM2-for-Windows reproduction that made this easy to confirm. That PR sized the encoding by the magnitude of the value rather than by the platform's CK_ULONG, which would have shortened CK_MAC_GENERAL_PARAMS and CK_RSA_PKCS_PSS_PARAMS on LP64; the binding sizes them from the C type instead. Thanks also to @Knacktus, whose #68 covers the enumeration failure this surfaced through. Refs #68, #103 — #103 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
signPSS returned errUnsupportedRSAOptions for rsa.PSSSaltLengthAuto, behind a TODO saying the biggest possible salt could in principle be worked out from the key. It can, and crypto/rsa already does it: (modulus_bits - 1 + 7)/8 - hash_length - 2 For a 4096-bit modulus and SHA-256 that is (4096-1+7)/8 - 32 - 2 = 478. The salt is the largest the encoded message can carry, so callers that pass the zero-valued PSSOptions.SaltLength — the crypto.Signer default, and what several callers hand us without thinking about salts at all — now get a signature rather than an error. Verifiers accept it either way: PSSSaltLengthAuto recovers the salt length from the encoding. The calculation moves into maxPSSSaltLength so it can be tested without a token, and differs from #96 in one respect. That PR computes the length as uint(k.N.BitLen()-1+7)/8 - 2 - hLen, all unsigned; a modulus too small for the chosen hash (SHA-512 at 521 bits or below) makes that subtraction wrap and yields a salt length near 2^64 instead of an error. Doing the arithmetic in int and reporting rsa.ErrMessageTooLong on a negative result matches what crypto/rsa returns for the same case. errUnsupportedRSAOptions is still returned for PSSSaltLengthAuto when the key's public half is not an *rsa.PublicKey, since there is no modulus to size the salt from. Both doc comments that described Auto as unsupported are updated. testRsaSigningPSS now runs each hash against both Auto and EqualsHash, and TestMaxPSSSaltLength covers the helper against the crypto/rsa expression for 1024- through 4096-bit keys across SHA-1..SHA-512, round-trips a signature through VerifyPSS with Auto, and pins the two error paths. It skips the 4096-bit key under -short, where generating it dominates the runtime. Note that a token is free to reject a salt length it does not implement; SoftHSMv3 does not advertise CKM_RSA_PKCS_PSS at all, so TestHardRSA's PSS subtests skip there and this path is covered natively. Fixes the TODO with the calculation from #96 by @maraino, who also supplied the worked 4096/SHA-256 example above and the crypto/rsa change it follows (https://golang.org/cl/302230). Refs #96 — #96 Co-Authored-By: Mariano Cano <mariano.cano@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Context keeps its *pool.ResourcePool unexported, along with every counter the pool maintains, so there is no way to see how the session pool is coping — issue #119 wanted them for metrics. The pool is vendored under internal/pool, so its types cannot appear in the public API at all: callers outside this module cannot name them. That rules out returning *pool.ResourcePool or forwarding its accessors, and leaves the second of the two shapes proposed in #119 — a struct of our own: func (c *Context) PoolStats() PoolStats PoolStats mirrors all nine of the pool's accessors (Capacity, Available, Active, InUse, MaxCapacity, WaitCount, WaitTime, IdleTimeout, IdleClosed) as a plain value with no reference back to the pool, so it can be kept, copied and marshalled. Durations marshal to JSON as nanoseconds, matching the pool's own StatsJSON, which is what the first shape in #119 would have returned. #119 asked whether reading stats might cause unnecessary communication with the token. It does not: every field is an atomic counter the pool already maintains in memory, so PoolStats makes no PKCS#11 call and takes no session from the pool. It is therefore safe on a metrics scrape path, concurrently with any other operation, and on a closed Context, where it reports the pool as it was torn down — Capacity zero, MaxCapacity intact. The fields are read one at a time rather than under a lock, so a snapshot taken under load is individually accurate but not internally consistent: Available and InUse need not add up to Capacity. All of this is in the doc comments, since none of it is guessable from the field names. Two fields need explaining rather than hiding. Capacity is one below the effective MaxSessions, because crypto11 holds a session back to keep the login state alive. IdleTimeout and IdleClosed are always zero, since Configure builds the pool with no idle timeout and nothing else changes it; they are reported anyway so the struct stays a faithful mirror if that ever becomes configurable, with a comment saying so. A nil pool returns the zero PoolStats rather than panicking. That is only reachable for a Context that did not come from Configure. Tests: TestPoolStatsFields drives all nine fields off a pool of fake resources, including a blocking Get to move WaitCount and WaitTime, so the mapping is covered without a token. TestPoolStatsMarshalsToJSON pins the nanosecond encoding, TestPoolStatsNoPool the zero-value Context. TestContextPoolStats then checks a real token: the counters before, from inside a withSession callback (InUse 1, Available down one, Active at least one), after the session is returned, and after Close. Proposed by @eriklupander in #119, including the PoolStats shape this follows and the question about talking to the device. Refs #119 — #119 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ming Some tokens return CKA_VALUE in a fixed-size buffer, null-padded past the end of the certificate it holds. x509.ParseCertificate rejects the padding as trailing data, so FindCertificate — and FindAllPairedCertificates, which reaches the same code through makeKeyPair — failed outright on those tokens. DER is self-delimiting, so the certificate's own length header says where it ends. findCertificate now hands the attribute to parseCertificateValue, which reads one ASN.1 element with asn1.Unmarshal into an asn1.RawValue, parses RawValue.FullBytes, and requires whatever follows to be null bytes. This differs from #106, which trims the buffer with bytes.Trim(raw, "\x00") before parsing. Content-based trimming cannot tell padding from certificate: the last byte of a certificate is the last byte of its signature, which is effectively random, so roughly one certificate in 256 legitimately ends in a null byte. Generating self-signed ECDSA certificates until one does — 81 attempts — and running both versions against it: baseline (no trim), unpadded: parses OK #106, unpadded: x509: malformed certificate #106, padded: x509: malformed certificate length-delimited, either way: parses OK So that one-in-256 breaks on tokens that pad, which the change was written for, and also breaks on tokens that do not, which worked before. bytes.Trim is bidirectional besides, so leading null bytes are stripped too: the buffer no longer starts with the certificate, and reinterpreting it silently is worse than reporting it. Trailing bytes that are not null are likewise an error here rather than something to discard — a truncated or misreported attribute should surface, not be papered over. Certificate parsing moves out of findCertificate so it can be tested without a token. TestParseCertificateValue covers plain DER, null-padded DER, and a certificate whose signature ends in a null byte both unpadded and padded — the case #106 gets wrong, generated by looping until one turns up rather than hardcoding a fixture. TestParseCertificateValueRejectsGarbage pins the error paths: non-null trailing data, leading null bytes, nil input, and a certificate truncated in half then null-padded back to its original length, which is precisely the corruption a padding-tolerant parser must not accept. Reported by @donachan-tesla in #106, with the null-padded tokens that make this reachable at all. Refs #106 — #106 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The four FindPrivateKey* methods have had no direct test coverage since they landed in #115 — the only exercise was one FindRSAPrivateKey subtest in rsa_test.go, which touches neither attribute matching nor argument validation. Port the tests proposed in PR #111, adapted to this branch's cryptoki binding: - TestFindKeysRequiresIdOrLabel gains assertions that FindPrivateKey and FindPrivateKeys reject a nil id *and* nil label. - TestFindingPrivateKeysWithAttributes covers matching on CkaLabel (single and shared) and filtering on CkaKeyType. Also add TestFindingPrivateKeyNotFound, which is not from #111. It pins the current not-found behaviour, where the singular lookups return an error while the plural ones return an empty slice. Note that this contradicts the doc comments on FindPrivateKey and FindPrivateKeyWithAttributes, which still promise "or nil if it cannot be found"; the test documents what the code does today rather than asserting it is correct. The tests are unchanged in intent from mandelsoft's original, which proposed them alongside a PrivateKey implementation that #115 has since superseded. #111 Co-Authored-By: Uwe Krueger <553075+mandelsoft@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r name Issue #112 asked for the generic FindKeyPair* family to return SignerDecrypter so existing key pairs could decrypt. PR #115 answered it with a parallel RSA-specific API instead of a breaking signature change, but never said so in the docs, so the issue stayed open for 19 months. Close the gap without touching the generic signatures: - Rename FindKeyRSAPairsWithAttributes to FindRSAKeyPairsWithAttributes. Every sibling is FindRSA*; this one was the odd one out, which made the RSA finders hard to grep for. Breaking rename, so v2 or never. - Add FindAllRSAKeyPairs, the decryption-capable counterpart of FindAllKeyPairs. "Give me everything on this token I can decrypt with" had no one-call form. - Cross-reference the two families: Signer and the FindKeyPair finders now point at SignerDecrypter and the FindRSA* finders, and SignerDecrypter points back at how to obtain one. Also fixes "crypto.Signer of SignerDecrypter" -> "or" in two rsa.go doc comments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 4, 2026
Closed
Closed
Closed
Five assertions in keys_test.go counted every matching object on the token rather than the ones the test created, so they only passed against a token that started empty. On a persistent token — CloudHSM, a shared dev HSM, or a rerun after a crash left keys behind — they failed: - TestFindingKeysWithAttributes, searching on CkaValueLen 16 and 32 - TestFindingKeyPairsWithAttributes, searching on CkaKeyType CKK_RSA - TestFindingPrivateKeysWithAttributes, the same search - TestFindingAllKeys and TestFindingAllKeyPairs Take the fix proposed in PR #79, by way of the idiom this branch already uses in TestFindAllCertificates: identify the objects by an attribute instead of counting them. #79 snapshotted a baseline count and asserted n + len(baseline), which still races against anything created concurrently and, more importantly, passes even when the finder returns the wrong keys. Matching on CKA_ID does not. Add two generic helpers, requireKeysFound and requireKeysAbsent, and convert the five sites. Keys whose CKA_ID cannot be read are skipped rather than failing the test, since they are by definition not ours. Searches by label keep their exact counts: the labels are randomBytes(), so those searches are already isolated and the count is the stronger assertion. The CkaValueLen searches also gain an exclusion check. require.Len was the only thing asserting that a 256-bit key does not answer a CkaValueLen=16 query; containment alone would have dropped that, so requireKeysAbsent now states it directly. TestFindingPrivateKeysWithAttributes is not in #79 — it landed in 62bbca8 and reproduced the same pattern. #79 Co-Authored-By: Rob Fitzpatrick <19215121+ProsaicSatsuma@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FindCertificate needs an id, label or serial, and FindAllPairedCertificates only returns certificates that have a matching private key. A caller that knows nothing about a token — the case PR #71 was opened for in August 2020 — had no way to list what is on it. Add the plain enumerator, symmetric with FindAllKeys and FindAllKeyPairs. Take the API from #71, with three changes to the implementation: - Page C_FindObjects instead of taking a single call's worth. The original patch stopped at maxHandlePerFind, silently returning 20 certificates on a token holding more. Enumeration now goes through findKeysWithAttributes, which already pages and is not key-specific, so the batching is right in one place rather than two. - Match only CKC_X_509 objects. A WTLS or attribute certificate is not an X.509 certificate and would fail x509.ParseCertificate, so a single one would hide every real certificate on the token — the shape of #68. The token filters them out instead. - Parse via parseCertificateValue, not x509.ParseCertificate directly, so the null-padded CKA_VALUE handling from d5490a7 applies here too. The original patch would have regressed on the tokens #106 reported. A CKA_VALUE that claims to be X.509 but does not parse is still an error rather than a skip: that is corruption, not a kind of certificate this package cannot represent. The error now names the object by CKA_ID and CKA_LABEL, since failing anonymously part-way through a token full of certificates says nothing about which one is at fault. The label is quoted, not interpolated raw — it is arbitrary bytes chosen by whoever wrote the object. Document that enumeration is not trust. This API's whole audience is the caller who knows nothing about the token, which is exactly the caller most likely to feed the result into a trust store; anyone able to write to the token can add a certificate. Tests import a few certificates and then one more than a batch, matching on full DER so the finder is shown to return the right bytes and not merely the right count. They delete only what they imported: #71 proposed a removeAllCertificates helper that destroys every certificate object on the token, which is not something a test should do to a shared or production HSM. #71 Co-Authored-By: mekpavit <21259908+mekpavit@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 4, 2026
Closed
FindCertificate returns one certificate and FindAllCertificates returns every certificate unordered, so a caller needing the issuers above a leaf — TLS being the obvious case — had to reassemble the chain itself. Add the walk, closing issue #91. Take the API and the algorithm from PR #83, which proposed it in June 2021 and cannot be rebased onto the current tree, with four changes: - Walk iteratively and skip certificates already placed. #83 recursed and guarded against repeats by scanning its own return value, which is always empty at the point it is read, so the guard never fired. Two CAs cross-signing each other recursed until the stack was exhausted, and anything able to write to the token decides whether that happens. - Settle the issuer by signature, not by name. #83 took the first object whose CKA_SUBJECT matched, so a token holding a renewed or cross-signed CA — two certificates, one distinguished name — returned whichever the token listed first and a chain that need not verify. - Return a short chain when the issuer is absent. #83's identifier fallback raised an error when nothing matched, so leaf plus intermediate on the token with the root in the system trust store — the ordinary arrangement — failed the call. Its test imported the root as well and never saw it. - Reuse findKeysWithAttributes and parseCertificateValue rather than adding a second pager and calling x509.ParseCertificate directly, which would have reverted d5490a7 for the null-padded CKA_VALUE of #106. Only run the identifier scan when the certificate names an authority key identifier: it costs a read of every certificate on the token, and an empty identifier matches every certificate that has no subject key identifier. FindAllCertificates and the new walk now share findX509Certificates, so the CKC_X_509 filter and the DER parsing stay in one place. The API is not extended to finding or deleting certificates by arbitrary attributes, which #83 also carried: that is unrelated to the chain and is its own decision. Tests cover the full chain, a missing root, a same-subject decoy CA imported ahead of the real one, a subject attribute that disagrees with the DER so only the key identifier can link the chain, and a cross-signed cycle. Removing the signature check, the identifier fallback or the already-placed guard each fails its test, the last by hanging. #91 #83 Co-Authored-By: Oleksandr Grytsov <oleksandr_grytsov@epam.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FindAllPairedCertificates built a tls.Certificate holding the leaf and nothing else, which is not enough to hand to crypto/tls: a peer that does not already hold the intermediate cannot verify a leaf on its own. Every caller serving TLS from a token had to fetch the issuers separately and splice them in, which is the work issue #91 asked the library to do. Fill Certificate from findIssuerChain, the walk added for FindCertificateChain. Certificate[0] is still the leaf, so a caller reading only the first entry is unaffected; the issuers the token holds follow it in the order crypto/tls sends them. The chain is what the token holds and no more. It ends where the issuers run out, so intermediates kept elsewhere are still the caller's to supply, and a self-signed root is returned rather than dropped — trimming a trailing entry that is its own issuer is one line for a caller who would rather not send it, whereas a root the library discarded cannot be recovered. Tests pair a generated key with a leaf by CKA_ID and import the two issuers above it under unrelated ids, so they can only be reached by the walk. Returning the leaf alone fails the test. #91 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
configfile tocrypto11.config.json