Summary
Introduce runtime-neutral BinaryLike and KeyLike types in src/types.ts, and use them across the public API in place of crypto.BinaryLike / crypto.KeyLike.
export type BinaryLike = crypto.BinaryLike | ArrayBuffer;
export type KeyLike = crypto.KeyLike | CryptoKey | Uint8Array;
Why
Today every public signature that touches key material or data to be signed is typed against node:crypto directly — SignedXmlOptions.privateKey, SignedXmlOptions.publicCert, GetKeyInfoContentArgs.publicCert, and both methods on the SignatureAlgorithm interface.
That hard-codes Node into the type surface. A caller cannot pass a CryptoKey, which is the only key representation the Web Crypto API produces, so the public types themselves block any Web Crypto backend before a line of implementation is written. This abstraction is a prerequisite for #477, and it is the piece of #513 that stands on its own regardless of what shape the rest of that work eventually takes.
Why 7.0
Altering types exported from src/types.ts is breaking for consumers, so this belongs in a major even though it is additive in practice. Widening a parameter type is source-compatible for callers, but it is not compatible for anyone who implements SignatureAlgorithm — third-party HSM- and KMS-backed implementations exist, and their method signatures have to widen to match.
Design note
In #513 these were introduced as blanket unions, which forced casts back out at every use site:
signer.update(signedInfo as crypto.BinaryLike);
const res = signer.sign(privateKey as crypto.KeyLike, "base64");
The cast is load-bearing: without it TypeScript correctly rejects the call (TS2345: Type 'CryptoKey' is not assignable to parameter of type 'KeyLike | SignKeyObjectInput | SignPrivateKeyInput'). So the widened type promises support that RsaSha1/RsaSha256/RsaSha512/HmacSha1 do not have, and the cast deletes the diagnostic that would have said so.
What happens at runtime differs by arm, and the CryptoKey case is the concerning one:
Uint8Array fails with ERR_OSSL_UNSUPPORTED: error:1E08010C:DECODER routines::unsupported — an opaque OpenSSL error a long way from the cause.
CryptoKey does not fail at all. Node accepts it and emits DEP0203 DeprecationWarning: Passing a CryptoKey to node:crypto functions is deprecated. It appears to work, tests pass, and it breaks whenever Node removes the shim — and it can never work off-Node, where node:crypto does not exist. A Node-backed algorithm would look like it supports Web Crypto keys while doing nothing of the kind.
Options that avoid the cast rather than suppressing it:
- Make the interface generic in its key type, so each implementation declares what it accepts.
- Keep the union, but narrow through a helper that validates and throws a clear error instead of an
as.
- Separate interfaces per backend, so a Node algorithm never claims to take a
CryptoKey at all.
Whichever way it goes, the goal is that each algorithm states what it actually accepts, rather than casting out of a union that promises more than it can take.
Scope
In scope:
- Define
BinaryLike / KeyLike in src/types.ts
- Apply them to
SignedXmlOptions, GetKeyInfoContentArgs, and the SignatureAlgorithm interface
- Decide how the bundled Node-backed algorithms narrow to concrete types without blanket casts
- Document the change for implementers of
SignatureAlgorithm
Out of scope — these get their own issues:
- Any Web Crypto algorithm implementation
- Async / callback API changes
HashAlgorithm changes
References
Summary
Introduce runtime-neutral
BinaryLikeandKeyLiketypes insrc/types.ts, and use them across the public API in place ofcrypto.BinaryLike/crypto.KeyLike.Why
Today every public signature that touches key material or data to be signed is typed against
node:cryptodirectly —SignedXmlOptions.privateKey,SignedXmlOptions.publicCert,GetKeyInfoContentArgs.publicCert, and both methods on theSignatureAlgorithminterface.That hard-codes Node into the type surface. A caller cannot pass a
CryptoKey, which is the only key representation the Web Crypto API produces, so the public types themselves block any Web Crypto backend before a line of implementation is written. This abstraction is a prerequisite for #477, and it is the piece of #513 that stands on its own regardless of what shape the rest of that work eventually takes.Why 7.0
Altering types exported from
src/types.tsis breaking for consumers, so this belongs in a major even though it is additive in practice. Widening a parameter type is source-compatible for callers, but it is not compatible for anyone who implementsSignatureAlgorithm— third-party HSM- and KMS-backed implementations exist, and their method signatures have to widen to match.Design note
In #513 these were introduced as blanket unions, which forced casts back out at every use site:
The cast is load-bearing: without it TypeScript correctly rejects the call (
TS2345: Type 'CryptoKey' is not assignable to parameter of type 'KeyLike | SignKeyObjectInput | SignPrivateKeyInput'). So the widened type promises support thatRsaSha1/RsaSha256/RsaSha512/HmacSha1do not have, and the cast deletes the diagnostic that would have said so.What happens at runtime differs by arm, and the
CryptoKeycase is the concerning one:Uint8Arrayfails withERR_OSSL_UNSUPPORTED: error:1E08010C:DECODER routines::unsupported— an opaque OpenSSL error a long way from the cause.CryptoKeydoes not fail at all. Node accepts it and emitsDEP0203 DeprecationWarning: Passing a CryptoKey to node:crypto functions is deprecated. It appears to work, tests pass, and it breaks whenever Node removes the shim — and it can never work off-Node, wherenode:cryptodoes not exist. A Node-backed algorithm would look like it supports Web Crypto keys while doing nothing of the kind.Options that avoid the cast rather than suppressing it:
as.CryptoKeyat all.Whichever way it goes, the goal is that each algorithm states what it actually accepts, rather than casting out of a union that promises more than it can take.
Scope
In scope:
BinaryLike/KeyLikeinsrc/types.tsSignedXmlOptions,GetKeyInfoContentArgs, and theSignatureAlgorithminterfaceSignatureAlgorithmOut of scope — these get their own issues:
HashAlgorithmchangesReferences