diff --git a/dist/index.cjs b/dist/index.cjs index 824843c..ea40603 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -37,6 +37,7 @@ __export(src_exports, { HTTPMessageSignaturesParseError: () => HTTPMessageSignaturesParseError, InvalidRequestError: () => InvalidRequestError, Pkcs1ParseError: () => Pkcs1ParseError, + Pkcs8ParseError: () => Pkcs8ParseError, RequestHasMultipleDateHeadersError: () => RequestHasMultipleDateHeadersError, RequestHasMultipleSignatureHeadersError: () => RequestHasMultipleSignatureHeadersError, SignatureHeaderNotFoundError: () => SignatureHeaderNotFoundError, @@ -66,9 +67,11 @@ __export(src_exports, { lcObjectKey: () => lcObjectKey, numberToUint8Array: () => numberToUint8Array, objectLcKeys: () => objectLcKeys, + parseAlgorithmIdentifier: () => parseAlgorithmIdentifier, parseDraftRequest: () => parseDraftRequest, parseDraftRequestSignatureHeader: () => parseDraftRequestSignatureHeader, parsePkcs1: () => parsePkcs1, + parsePkcs8: () => parsePkcs8, parsePublicKey: () => parsePublicKey, parseRequestSignature: () => parseRequestSignature, parseSpki: () => parseSpki, @@ -798,29 +801,34 @@ function decodePem(input) { const der = typeof input === "string" ? reHex.test(input) ? import_hex.default.decode(input) : import_base64.default.unarmor(input) : input; return der; } -function parseSpki(input) { - const parsed = import_asn1js2.default.decode(decodePem(input)); - if (!parsed.sub || parsed.sub.length === 0 || parsed.sub.length > 2) - throw new SpkiParseError("Invalid SPKI (invalid sub)"); - const algorithmIdentifierSub = parsed.sub && parsed.sub[0] && parsed.sub[0].sub; +function parseAlgorithmIdentifier(input) { + const algorithmIdentifierSub = input.sub; if (!algorithmIdentifierSub) - throw new SpkiParseError("Invalid SPKI (no AlgorithmIdentifier)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier"); if (algorithmIdentifierSub.length === 0) - throw new SpkiParseError("Invalid SPKI (invalid AlgorithmIdentifier sub length, zero)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (sub length, zero)"); if (algorithmIdentifierSub.length > 2) - throw new SpkiParseError("Invalid SPKI (invalid AlgorithmIdentifier sub length, too many)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (sub length, too many)"); if (algorithmIdentifierSub[0].tag.tagNumber !== 6) - throw new SpkiParseError("Invalid SPKI (invalid AlgorithmIdentifier.sub[0] type)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (.sub[0] type)"); const algorithm = algorithmIdentifierSub[0]?.content() ?? null; if (typeof algorithm !== "string") - throw new SpkiParseError("Invalid SPKI (invalid algorithm content)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (invalid content)"); const parameter = algorithmIdentifierSub[1]?.content() ?? null; return { - der: asn1ToArrayBuffer(parsed), algorithm, parameter }; } +function parseSpki(input) { + const parsed = import_asn1js2.default.decode(decodePem(input)); + if (!parsed.sub || parsed.sub.length === 0 || parsed.sub.length > 2) + throw new SpkiParseError("Invalid SPKI (invalid sub)"); + return { + der: asn1ToArrayBuffer(parsed), + ...parseAlgorithmIdentifier(parsed.sub[0]) + }; +} function parsePublicKey(input) { try { return parseSpki(input); @@ -916,6 +924,36 @@ async function webVerifyDraftSignature(parsed, publicKeyPem, errorLogger) { return false; } } + +// src/pem/pkcs8.ts +var import_asn1js3 = __toESM(require("@lapo/asn1js"), 1); +var Pkcs8ParseError = class extends Error { + constructor(message) { + super(message); + } +}; +function parsePkcs8(input) { + const parsed = import_asn1js3.default.decode(decodePem(input)); + if (!parsed.sub || parsed.sub.length < 3 || parsed.sub.length > 4) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid sub length)"); + const version = parsed.sub[0]; + if (!version || !version.tag || version.tag.tagNumber !== 2) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid version)"); + const privateKeyAlgorithm = parseAlgorithmIdentifier(parsed.sub[1]); + const privateKey = parsed.sub[2]; + if (!privateKey || !privateKey.tag || privateKey.tag.tagNumber !== 4) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid privateKey)"); + const attributes = parsed.sub[3]; + if (attributes) { + if (attributes.tag.tagNumber !== 49) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid attributes)"); + } + return { + privateKey: asn1ToArrayBuffer(privateKey), + ...privateKeyAlgorithm, + attributesRaw: attributes ? asn1ToArrayBuffer(attributes) : null + }; +} // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ClockSkewInvalidError, @@ -925,6 +963,7 @@ async function webVerifyDraftSignature(parsed, publicKeyPem, errorLogger) { HTTPMessageSignaturesParseError, InvalidRequestError, Pkcs1ParseError, + Pkcs8ParseError, RequestHasMultipleDateHeadersError, RequestHasMultipleSignatureHeadersError, SignatureHeaderNotFoundError, @@ -954,9 +993,11 @@ async function webVerifyDraftSignature(parsed, publicKeyPem, errorLogger) { lcObjectKey, numberToUint8Array, objectLcKeys, + parseAlgorithmIdentifier, parseDraftRequest, parseDraftRequestSignatureHeader, parsePkcs1, + parsePkcs8, parsePublicKey, parseRequestSignature, parseSpki, diff --git a/dist/index.d.ts b/dist/index.d.ts index 05dea47..8e03186 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -15,3 +15,4 @@ export * from './rfc9421/verify.js'; */ export * from './pem/spki.js'; export * from './pem/pkcs1.js'; +export * from './pem/pkcs8.js'; diff --git a/dist/index.mjs b/dist/index.mjs index f31c431..37f6bcc 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -709,29 +709,34 @@ function decodePem(input) { const der = typeof input === "string" ? reHex.test(input) ? Hex.decode(input) : Base64.unarmor(input) : input; return der; } -function parseSpki(input) { - const parsed = ASN12.decode(decodePem(input)); - if (!parsed.sub || parsed.sub.length === 0 || parsed.sub.length > 2) - throw new SpkiParseError("Invalid SPKI (invalid sub)"); - const algorithmIdentifierSub = parsed.sub && parsed.sub[0] && parsed.sub[0].sub; +function parseAlgorithmIdentifier(input) { + const algorithmIdentifierSub = input.sub; if (!algorithmIdentifierSub) - throw new SpkiParseError("Invalid SPKI (no AlgorithmIdentifier)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier"); if (algorithmIdentifierSub.length === 0) - throw new SpkiParseError("Invalid SPKI (invalid AlgorithmIdentifier sub length, zero)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (sub length, zero)"); if (algorithmIdentifierSub.length > 2) - throw new SpkiParseError("Invalid SPKI (invalid AlgorithmIdentifier sub length, too many)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (sub length, too many)"); if (algorithmIdentifierSub[0].tag.tagNumber !== 6) - throw new SpkiParseError("Invalid SPKI (invalid AlgorithmIdentifier.sub[0] type)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (.sub[0] type)"); const algorithm = algorithmIdentifierSub[0]?.content() ?? null; if (typeof algorithm !== "string") - throw new SpkiParseError("Invalid SPKI (invalid algorithm content)"); + throw new SpkiParseError("Invalid AlgorithmIdentifier (invalid content)"); const parameter = algorithmIdentifierSub[1]?.content() ?? null; return { - der: asn1ToArrayBuffer(parsed), algorithm, parameter }; } +function parseSpki(input) { + const parsed = ASN12.decode(decodePem(input)); + if (!parsed.sub || parsed.sub.length === 0 || parsed.sub.length > 2) + throw new SpkiParseError("Invalid SPKI (invalid sub)"); + return { + der: asn1ToArrayBuffer(parsed), + ...parseAlgorithmIdentifier(parsed.sub[0]) + }; +} function parsePublicKey(input) { try { return parseSpki(input); @@ -827,6 +832,36 @@ async function webVerifyDraftSignature(parsed, publicKeyPem, errorLogger) { return false; } } + +// src/pem/pkcs8.ts +import ASN13 from "@lapo/asn1js"; +var Pkcs8ParseError = class extends Error { + constructor(message) { + super(message); + } +}; +function parsePkcs8(input) { + const parsed = ASN13.decode(decodePem(input)); + if (!parsed.sub || parsed.sub.length < 3 || parsed.sub.length > 4) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid sub length)"); + const version = parsed.sub[0]; + if (!version || !version.tag || version.tag.tagNumber !== 2) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid version)"); + const privateKeyAlgorithm = parseAlgorithmIdentifier(parsed.sub[1]); + const privateKey = parsed.sub[2]; + if (!privateKey || !privateKey.tag || privateKey.tag.tagNumber !== 4) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid privateKey)"); + const attributes = parsed.sub[3]; + if (attributes) { + if (attributes.tag.tagNumber !== 49) + throw new Pkcs8ParseError("Invalid PKCS#8 (invalid attributes)"); + } + return { + privateKey: asn1ToArrayBuffer(privateKey), + ...privateKeyAlgorithm, + attributesRaw: attributes ? asn1ToArrayBuffer(attributes) : null + }; +} export { ClockSkewInvalidError, DraftSignatureHeaderClockInvalidError, @@ -835,6 +870,7 @@ export { HTTPMessageSignaturesParseError, InvalidRequestError, Pkcs1ParseError, + Pkcs8ParseError, RequestHasMultipleDateHeadersError, RequestHasMultipleSignatureHeadersError, SignatureHeaderNotFoundError, @@ -864,9 +900,11 @@ export { lcObjectKey, numberToUint8Array, objectLcKeys, + parseAlgorithmIdentifier, parseDraftRequest, parseDraftRequestSignatureHeader, parsePkcs1, + parsePkcs8, parsePublicKey, parseRequestSignature, parseSpki, diff --git a/dist/pem/pkcs1.d.ts b/dist/pem/pkcs1.d.ts index 7cfa1c3..d16e5f9 100644 --- a/dist/pem/pkcs1.d.ts +++ b/dist/pem/pkcs1.d.ts @@ -2,6 +2,9 @@ import ASN1 from '@lapo/asn1js'; export declare class Pkcs1ParseError extends Error { constructor(message: string); } +/** + * Parse PKCS#1 public key + */ export declare function parsePkcs1(input: ASN1.StreamOrBinary): { pkcs1: ArrayBufferLike; modulus: number; diff --git a/dist/pem/pkcs8.d.ts b/dist/pem/pkcs8.d.ts new file mode 100644 index 0000000..9fa1639 --- /dev/null +++ b/dist/pem/pkcs8.d.ts @@ -0,0 +1,42 @@ +import ASN1 from '@lapo/asn1js'; +export declare class Pkcs8ParseError extends Error { + constructor(message: string); +} +/** + * Parse PKCS#8 private key + * + * PrivateKeyInfo ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}}, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL } + * + * PrivateKey ::= OCTET STRING + * Version ::= INTEGER + * Attributes ::= SET OF Attribute + * @param input + * @returns + */ +export declare function parsePkcs8(input: ASN1.StreamOrBinary): { + attributesRaw: ArrayBuffer | null; + algorithm: string; + parameter: any; + privateKey: ArrayBufferLike; +} | { + attributesRaw: ArrayBuffer | null; + algorithm: "1.2.840.113549.1.1.1\nrsaEncryption\nPKCS #1"; + parameter: null; + privateKey: ArrayBufferLike; +} | { + attributesRaw: ArrayBuffer | null; + der: ArrayBuffer; + algorithm: "1.3.101.112\ncurveEd25519\nEdDSA 25519 signature algorithm"; + parameter: null; + privateKey: ArrayBufferLike; +} | { + attributesRaw: ArrayBuffer | null; + der: ArrayBuffer; + algorithm: "1.2.840.10045.2.1\necPublicKey\nANSI X9.62 public key type"; + parameter: "1.2.840.10045.3.1.7\nprime256v1\nANSI X9.62 named elliptic curve"; + privateKey: ArrayBufferLike; +}; diff --git a/dist/pem/spki.d.ts b/dist/pem/spki.d.ts index 66603fe..1290cd2 100644 --- a/dist/pem/spki.d.ts +++ b/dist/pem/spki.d.ts @@ -17,13 +17,15 @@ export declare function getPublicKeyAlgorithmNameFromOid(oidStr: string): "RSASS * (Most environments may implement only P-256, P-384 and P-521) */ export declare function getNistCurveFromOid(oidStr: string): "P-192" | "P-224" | "P-256" | "P-384" | "P-521"; -export type SpkiParsedAlgorithmIdentifierBase = { - /** - * DER - * - * (Somehow crypto.createPublicKey will cause `error:1E08010C:DECODER routines::unsupported`) - */ - der: ArrayBuffer; +/** + * Convert ASN1(@lapo/asn1js).Binary to ArrayBuffer + * + * @param asn1 ASN1 object + * @param contentOnly If true, return content only, excluding tag and length + * @examples `asn1BinaryToArrayBuffer(ASN1.decode(der).stream.enc);` + */ +export declare function asn1ToArrayBuffer(asn1: ASN1, contentOnly?: boolean): ArrayBufferLike; +export type ParsedAlgorithmIdentifierBase = { /** * Parsed algorithm, 3 lines string * Data from https://github.com/lapo-luchini/asn1js/blob/trunk/oids.js @@ -42,31 +44,31 @@ export type SpkiParsedAlgorithmIdentifierBase = { */ parameter: any; }; -/** - * Convert ASN1(@lapo/asn1js).Binary to ArrayBuffer - * - * @param asn1 ASN1 object - * @param contentOnly If true, return content only, excluding tag and length - * @examples `asn1BinaryToArrayBuffer(ASN1.decode(der).stream.enc);` - */ -export declare function asn1ToArrayBuffer(asn1: ASN1, contentOnly?: boolean): ArrayBufferLike; -export type SpkiParsedRSAIdentifier = { - der: ArrayBuffer; +export type ParsedRSAIdentifier = { algorithm: '1.2.840.113549.1.1.1\nrsaEncryption\nPKCS #1'; parameter: null; }; -export type SpkiParsedEd25519Identifier = { +export type ParsedEd25519Identifier = { der: ArrayBuffer; algorithm: '1.3.101.112\ncurveEd25519\nEdDSA 25519 signature algorithm'; parameter: null; }; -export type SpkiParsedNPrime256v1Identifier = { +export type ParsedNPrime256v1Identifier = { der: ArrayBuffer; algorithm: '1.2.840.10045.2.1\necPublicKey\nANSI X9.62 public key type'; parameter: '1.2.840.10045.3.1.7\nprime256v1\nANSI X9.62 named elliptic curve'; }; -export type SpkiParsedAlgorithmIdentifier = SpkiParsedRSAIdentifier | SpkiParsedEd25519Identifier | SpkiParsedNPrime256v1Identifier | SpkiParsedAlgorithmIdentifierBase; +export type ParsedAlgorithmIdentifier = ParsedRSAIdentifier | ParsedEd25519Identifier | ParsedNPrime256v1Identifier | ParsedAlgorithmIdentifierBase; +export type SpkiParsedAlgorithmIdentifier = ParsedAlgorithmIdentifierBase & { + /** + * DER + * + * (Somehow crypto.createPublicKey will cause `error:1E08010C:DECODER routines::unsupported`) + */ + der: ArrayBuffer; +}; export declare function decodePem(input: ASN1.StreamOrBinary): Exclude; +export declare function parseAlgorithmIdentifier(input: ASN1): ParsedAlgorithmIdentifier; /** * Parse X.509 SubjectPublicKeyInfo (SPKI) public key * @param input SPKI public key PEM or DER diff --git a/package.json b/package.json index c81ae42..469f686 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@misskey-dev/node-http-message-signatures", - "version": "0.0.0-alpha.13", + "version": "0.0.0-alpha.14", "description": "", "type": "module", "keywords": [