diff --git a/toolkit/components/certviewer/content/certDecoder.mjs b/toolkit/components/certviewer/content/certDecoder.mjs --- a/toolkit/components/certviewer/content/certDecoder.mjs +++ b/toolkit/components/certviewer/content/certDecoder.mjs @@ -5,10 +5,11 @@ import { Certificate, ECNamedCurves, ECPublicKey, RSAPublicKey, + MLDSAPublicKey, } from "./vendor/pkijs.js"; const getTimeZone = () => { let timeZone = new Date().toString().match(/\(([A-Za-z\s].*)\)/); if (timeZone === null) { @@ -45,10 +46,19 @@ x, // x coordinate y, // y coordinate xy: `04:${x}:${y}`, // 04 (uncompressed) public key }; } + if (publicKey instanceof MLDSAPublicKey) { + let keyHex = publicKey.rhoT1.valueBlock.valueHex; + let keyBytes = new Uint8Array(keyHex); + return { + kty: publicKey.alg, + keysize: keyBytes.length, + rhoT1: hashify(keyHex), + }; + } return { kty: "Unknown" }; }; const getX509Ext = (extensions, v) => { for (var extension in extensions) { @@ -1132,10 +1142,13 @@ "2.16.840.1.101.3.4.3.2": "DSA with SHA-256", "1.2.840.10045.4.1": "ECDSA with SHA-1", "1.2.840.10045.4.3.2": "ECDSA with SHA-256", "1.2.840.10045.4.3.3": "ECDSA with SHA-384", "1.2.840.10045.4.3.4": "ECDSA with SHA-512", + "2.16.840.1.101.3.4.3.17": "ML-DSA-44", + "2.16.840.1.101.3.4.3.18": "ML-DSA-65", + "2.16.840.1.101.3.4.3.19": "ML-DSA-87", }, aia: { "1.3.6.1.5.5.7.48.1": "Online Certificate Status Protocol (OCSP)", "1.3.6.1.5.5.7.48.2": "CA Issuers", diff --git a/toolkit/components/certviewer/content/certviewer.mjs b/toolkit/components/certviewer/content/certviewer.mjs --- a/toolkit/components/certviewer/content/certviewer.mjs +++ b/toolkit/components/certviewer/content/certviewer.mjs @@ -74,10 +74,23 @@ } } return result ? result : false; }; +const getMLDSASecurityLevel = signatureName => { + switch (signatureName) { + case "ML-DSA-44": + return "Level 2 (NIST)"; + case "ML-DSA-65": + return "Level 3 (NIST)"; + case "ML-DSA-87": + return "Level 5 (NIST)"; + default: + return null; + } +}; + export const adjustCertInformation = cert => { let certItems = []; let tabName = cert?.subject?.cn || ""; if (cert && !tabName) { // No common name, use the value of the last item in the cert's entries. @@ -173,10 +186,15 @@ createEntryItem("key-size", cert.subjectPublicKeyInfo.keysize), createEntryItem("curve", cert.subjectPublicKeyInfo.crv), createEntryItem("public-value", cert.subjectPublicKeyInfo.xy, true), createEntryItem("exponent", cert.subjectPublicKeyInfo.e), createEntryItem("modulus", cert.subjectPublicKeyInfo.n, true), + createEntryItem( + "mldsa-public-value", + cert.subjectPublicKeyInfo.rhoT1, + true + ), ].filter(elem => elem != null); } return items; }, certItems, @@ -190,14 +208,23 @@ createEntryItem("serial-number", cert.serialNumber, true), createEntryItem( "signature-algorithm", cert.signature ? cert.signature.name : null ), + ]; + + const secLvl = getMLDSASecurityLevel(cert.signature?.name); + if (secLvl) { + items.push(createEntryItem("security-level", secLvl)); + } + + items.push( createEntryItem("version", cert.version), - createEntryItem("download", cert.files ? cert.files.pem : null), - ].filter(elem => elem != null); - return items; + createEntryItem("download", cert.files ? cert.files.pem : null) + ); + + return items.filter(elem => elem != null); }, certItems, "miscellaneous", false ); diff --git a/toolkit/components/certviewer/content/vendor/pkijs.js b/toolkit/components/certviewer/content/vendor/pkijs.js --- a/toolkit/components/certviewer/content/vendor/pkijs.js +++ b/toolkit/components/certviewer/content/vendor/pkijs.js @@ -8609,10 +8609,90 @@ this.publicExponent = new Integer({ valueHex: stringToArrayBuffer(fromBase64(json.e, true)).slice(0, 3) }); } } RSAPublicKey.CLASS_NAME = "RSAPublicKey"; +/* @see https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-11.html */ +const RHO_T1 = "rhoT1"; +const ALG = "alg"; +const CLEAR_PROPS_MLDSA = [RHO_T1, ALG]; +const MLDSA_MIN_LENGTH = 32; +class MLDSAPublicKey extends PkiObject { + constructor(parameters = {}) { + super(); + + this.rhoT1 = getParametersValue(parameters, RHO_T1, MLDSAPublicKey.defaultValues(RHO_T1)); + this.alg = getParametersValue(parameters, ALG, MLDSAPublicKey.defaultValues(ALG)); + + if (parameters.json) { + this.fromJSON(parameters.json); + } + + if (parameters.schema) { + this.fromSchema(parameters.schema); + } + } + + static defaultValues(memberName) { + switch (memberName) { + case RHO_T1: + return new BitString(); + case ALG: + return ""; + default: + return super.defaultValues(memberName); + } + } + + static schema(parameters = {}) { + const names = getParametersValue(parameters, "names", {}); + return new BitString({ name: names.rhoT1 || RHO_T1 }); + } + + fromSchema(schema) { + clearProps(schema, CLEAR_PROPS_MLDSA); + + const asn1 = compareSchema(schema, schema, MLDSAPublicKey.schema({ + names: { rhoT1: RHO_T1 } + })); + + AsnError.assertSchema(asn1, this.className); + + const bitString = asn1.result.rhoT1; + const length = bitString.valueBlock.valueHexView.length; + + if (length < MLDSA_MIN_LENGTH || (length - MLDSA_MIN_LENGTH) % 320 !== 0) { + throw new Error(`Invalid ML-DSA key length: ${length} bytes`); + } + + this.rhoT1 = bitString; + } + + toSchema() { + return this.rhoT1; + } + + toJSON() { + return { + rhoT1: Convert.ToBase64Url(this.rhoT1.valueBlock.valueHexView), + alg: this.alg + }; + } + + fromJSON(json) { + ParameterError.assert("json", json, "rhoT1"); + const rawBuffer = stringToArrayBuffer(fromBase64(json.rhoT1, true)); + + if (rawBuffer.byteLength < MLDSA_MIN_LENGTH || (rawBuffer.byteLength - MLDSA_MIN_LENGTH) % 320 !== 0) { + throw new Error(`Invalid ML-DSA key length: ${rawBuffer.byteLength} bytes`); + } + + this.rhoT1 = new BitString({ valueHex: rawBuffer }); + } +} +MLDSAPublicKey.CLASS_NAME = "MLDSAPublicKey"; + const ALGORITHM$1 = "algorithm"; const SUBJECT_PUBLIC_KEY = "subjectPublicKey"; const CLEAR_PROPS$1a = [ALGORITHM$1, SUBJECT_PUBLIC_KEY]; class PublicKeyInfo extends PkiObject { constructor(parameters = {}) { @@ -8657,10 +8737,22 @@ catch (ex) { } } } break; + case "2.16.840.1.101.3.4.3.17": + /* Already a bitstring */ + this._parsedKey = new MLDSAPublicKey({ rhoT1: this.subjectPublicKey, alg: "ML-DSA-44" }); + break; + case "2.16.840.1.101.3.4.3.18": + /* Already a bitstring */ + this._parsedKey = new MLDSAPublicKey({ rhoT1: this.subjectPublicKey, alg: "ML-DSA-65" }); + break; + case "2.16.840.1.101.3.4.3.19": + /* Already a bitstring */ + this._parsedKey = new MLDSAPublicKey({ rhoT1: this.subjectPublicKey, alg: "ML-DSA-87" }); + break; } this._parsedKey || (this._parsedKey = null); } return this._parsedKey || undefined; } @@ -8724,10 +8816,19 @@ jwk.kty = "EC"; break; case "1.2.840.113549.1.1.1": jwk.kty = "RSA"; break; + case "2.16.840.1.101.3.4.3.17": + jwk.kty = "ML-DSA-44"; + break; + case "2.16.840.1.101.3.4.3.18": + jwk.kty = "ML-DSA-65"; + break; + case "2.16.840.1.101.3.4.3.19": + jwk.kty = "ML-DSA-87"; + break; } const publicKeyJWK = this.parsedKey.toJSON(); Object.assign(jwk, publicKeyJWK); return jwk; } @@ -8746,10 +8847,31 @@ this.algorithm = new AlgorithmIdentifier({ algorithmId: "1.2.840.113549.1.1.1", algorithmParams: new Null() }); break; + case "ML-DSA-44": + this.parsedKey = new MLDSAPublicKey({ json }); + this.algorithm = new AlgorithmIdentifier({ + algorithmId: "2.16.840.1.101.3.4.3.17", + algorithmParams: new Null() + }); + break; + case "ML-DSA-65": + this.parsedKey = new MLDSAPublicKey({ json }); + this.algorithm = new AlgorithmIdentifier({ + algorithmId: "2.16.840.1.101.3.4.3.18", + algorithmParams: new Null() + }); + break; + case "ML-DSA-87": + this.parsedKey = new MLDSAPublicKey({ json }); + this.algorithm = new AlgorithmIdentifier({ + algorithmId: "2.16.840.1.101.3.4.3.19", + algorithmParams: new Null() + }); + break; default: throw new Error(`Invalid value for "kty" parameter: ${json.kty}`); } this.subjectPublicKey = new BitString({ valueHex: this.parsedKey.toSchema().toBER(false) }); } @@ -24078,6 +24200,6 @@ } } initCryptoEngine(); -export { AbstractCryptoEngine, AccessDescription, Accuracy, AlgorithmIdentifier, AltName, ArgumentError, AsnError, AttCertValidityPeriod, Attribute, AttributeCertificateInfoV1, AttributeCertificateInfoV2, AttributeCertificateV1, AttributeCertificateV2, AttributeTypeAndValue, AuthenticatedSafe, AuthorityKeyIdentifier, BasicConstraints, BasicOCSPResponse, CAVersion, CRLBag, CRLDistributionPoints, CertBag, CertID, Certificate, CertificateChainValidationEngine, CertificatePolicies, CertificateRevocationList, CertificateSet, CertificateTemplate, CertificationRequest, ChainValidationCode, ChainValidationError, ContentInfo, CryptoEngine, DigestInfo, DistributionPoint, ECCCMSSharedInfo, ECNamedCurves, ECPrivateKey, ECPublicKey, EncapsulatedContentInfo, EncryptedContentInfo, EncryptedData, EnvelopedData, ExtKeyUsage, Extension, ExtensionValueFactory, Extensions, GeneralName, GeneralNames, GeneralSubtree, HASHED_MESSAGE, HASH_ALGORITHM, Holder, InfoAccess, IssuerAndSerialNumber, IssuerSerial, IssuingDistributionPoint, KEKIdentifier, KEKRecipientInfo, KeyAgreeRecipientIdentifier, KeyAgreeRecipientInfo, KeyBag, KeyTransRecipientInfo, MICROS, MILLIS, MacData, MessageImprint, NameConstraints, OCSPRequest, OCSPResponse, ObjectDigestInfo, OriginatorIdentifierOrKey, OriginatorInfo, OriginatorPublicKey, OtherCertificateFormat, OtherKeyAttribute, OtherPrimeInfo, OtherRecipientInfo, OtherRevocationInfoFormat, PBES2Params, PBKDF2Params, PFX, PKCS8ShroudedKeyBag, PKIStatus, PKIStatusInfo, POLICY_IDENTIFIER, POLICY_QUALIFIERS, ParameterError, PasswordRecipientinfo, PkiObject, PolicyConstraints, PolicyInformation, PolicyMapping, PolicyMappings, PolicyQualifierInfo, PrivateKeyInfo, PrivateKeyUsagePeriod, PublicKeyInfo, QCStatement, QCStatements, RDN, RSAESOAEPParams, RSAPrivateKey, RSAPublicKey, RSASSAPSSParams, RecipientEncryptedKey, RecipientEncryptedKeys, RecipientIdentifier, RecipientInfo, RecipientKeyIdentifier, RelativeDistinguishedNames, Request, ResponseBytes, ResponseData, RevocationInfoChoices, RevokedCertificate, SECONDS, SafeBag, SafeBagValueFactory, SafeContents, SecretBag, Signature, SignedAndUnsignedAttributes, SignedCertificateTimestamp, SignedCertificateTimestampList, SignedData, SignedDataVerifyError, SignerInfo, SingleResponse, SubjectDirectoryAttributes, TBSRequest, TSTInfo, TYPE$4 as TYPE, TYPE_AND_VALUES, Time, TimeStampReq, TimeStampResp, TimeType, V2Form, VALUE$5 as VALUE, VALUE_BEFORE_DECODE, checkCA, createCMSECDSASignature, createECDSASignatureFromCMS, engine, getAlgorithmByOID, getAlgorithmParameters, getCrypto, getEngine, getHashAlgorithm, getOIDByAlgorithm, getRandomValues, id_AnyPolicy, id_AuthorityInfoAccess, id_AuthorityKeyIdentifier, id_BaseCRLNumber, id_BasicConstraints, id_CRLBag_X509CRL, id_CRLDistributionPoints, id_CRLNumber, id_CRLReason, id_CertBag_AttributeCertificate, id_CertBag_SDSICertificate, id_CertBag_X509Certificate, id_CertificateIssuer, id_CertificatePolicies, id_ContentType_Data, id_ContentType_EncryptedData, id_ContentType_EnvelopedData, id_ContentType_SignedData, id_ExtKeyUsage, id_FreshestCRL, id_InhibitAnyPolicy, id_InvalidityDate, id_IssuerAltName, id_IssuingDistributionPoint, id_KeyUsage, id_MicrosoftAppPolicies, id_MicrosoftCaVersion, id_MicrosoftCertTemplateV1, id_MicrosoftCertTemplateV2, id_MicrosoftPrevCaCertHash, id_NameConstraints, id_PKIX_OCSP_Basic, id_PolicyConstraints, id_PolicyMappings, id_PrivateKeyUsagePeriod, id_QCStatements, id_SignedCertificateTimestampList, id_SubjectAltName, id_SubjectDirectoryAttributes, id_SubjectInfoAccess, id_SubjectKeyIdentifier, id_ad, id_ad_caIssuers, id_ad_ocsp, id_eContentType_TSTInfo, id_pkix, id_sha1, id_sha256, id_sha384, id_sha512, kdf, setEngine, stringPrep, verifySCTsForCertificate }; +export { AbstractCryptoEngine, AccessDescription, Accuracy, AlgorithmIdentifier, AltName, ArgumentError, AsnError, AttCertValidityPeriod, Attribute, AttributeCertificateInfoV1, AttributeCertificateInfoV2, AttributeCertificateV1, AttributeCertificateV2, AttributeTypeAndValue, AuthenticatedSafe, AuthorityKeyIdentifier, BasicConstraints, BasicOCSPResponse, CAVersion, CRLBag, CRLDistributionPoints, CertBag, CertID, Certificate, CertificateChainValidationEngine, CertificatePolicies, CertificateRevocationList, CertificateSet, CertificateTemplate, CertificationRequest, ChainValidationCode, ChainValidationError, ContentInfo, CryptoEngine, DigestInfo, DistributionPoint, ECCCMSSharedInfo, ECNamedCurves, ECPrivateKey, ECPublicKey, EncapsulatedContentInfo, EncryptedContentInfo, EncryptedData, EnvelopedData, ExtKeyUsage, Extension, ExtensionValueFactory, Extensions, GeneralName, GeneralNames, GeneralSubtree, HASHED_MESSAGE, HASH_ALGORITHM, Holder, InfoAccess, IssuerAndSerialNumber, IssuerSerial, IssuingDistributionPoint, KEKIdentifier, KEKRecipientInfo, KeyAgreeRecipientIdentifier, KeyAgreeRecipientInfo, KeyBag, KeyTransRecipientInfo, MICROS, MILLIS, MacData, MessageImprint, NameConstraints, OCSPRequest, OCSPResponse, ObjectDigestInfo, OriginatorIdentifierOrKey, OriginatorInfo, OriginatorPublicKey, OtherCertificateFormat, OtherKeyAttribute, OtherPrimeInfo, OtherRecipientInfo, OtherRevocationInfoFormat, PBES2Params, PBKDF2Params, PFX, PKCS8ShroudedKeyBag, PKIStatus, PKIStatusInfo, POLICY_IDENTIFIER, POLICY_QUALIFIERS, ParameterError, PasswordRecipientinfo, PkiObject, PolicyConstraints, PolicyInformation, PolicyMapping, PolicyMappings, PolicyQualifierInfo, PrivateKeyInfo, PrivateKeyUsagePeriod, PublicKeyInfo, QCStatement, QCStatements, RDN, RSAESOAEPParams, RSAPrivateKey, RSAPublicKey, RSASSAPSSParams, RecipientEncryptedKey, RecipientEncryptedKeys, RecipientIdentifier, RecipientInfo, RecipientKeyIdentifier, RelativeDistinguishedNames, Request, ResponseBytes, ResponseData, RevocationInfoChoices, RevokedCertificate, SECONDS, SafeBag, SafeBagValueFactory, SafeContents, SecretBag, Signature, SignedAndUnsignedAttributes, SignedCertificateTimestamp, SignedCertificateTimestampList, SignedData, SignedDataVerifyError, SignerInfo, SingleResponse, SubjectDirectoryAttributes, TBSRequest, TSTInfo, TYPE$4 as TYPE, TYPE_AND_VALUES, Time, TimeStampReq, TimeStampResp, TimeType, V2Form, VALUE$5 as VALUE, VALUE_BEFORE_DECODE, checkCA, createCMSECDSASignature, createECDSASignatureFromCMS, engine, getAlgorithmByOID, getAlgorithmParameters, getCrypto, getEngine, getHashAlgorithm, getOIDByAlgorithm, getRandomValues, id_AnyPolicy, id_AuthorityInfoAccess, id_AuthorityKeyIdentifier, id_BaseCRLNumber, id_BasicConstraints, id_CRLBag_X509CRL, id_CRLDistributionPoints, id_CRLNumber, id_CRLReason, id_CertBag_AttributeCertificate, id_CertBag_SDSICertificate, id_CertBag_X509Certificate, id_CertificateIssuer, id_CertificatePolicies, id_ContentType_Data, id_ContentType_EncryptedData, id_ContentType_EnvelopedData, id_ContentType_SignedData, id_ExtKeyUsage, id_FreshestCRL, id_InhibitAnyPolicy, id_InvalidityDate, id_IssuerAltName, id_IssuingDistributionPoint, id_KeyUsage, id_MicrosoftAppPolicies, id_MicrosoftCaVersion, id_MicrosoftCertTemplateV1, id_MicrosoftCertTemplateV2, id_MicrosoftPrevCaCertHash, id_NameConstraints, id_PKIX_OCSP_Basic, id_PolicyConstraints, id_PolicyMappings, id_PrivateKeyUsagePeriod, id_QCStatements, id_SignedCertificateTimestampList, id_SubjectAltName, id_SubjectDirectoryAttributes, id_SubjectInfoAccess, id_SubjectKeyIdentifier, id_ad, id_ad_caIssuers, id_ad_ocsp, id_eContentType_TSTInfo, id_pkix, id_sha1, id_sha256, id_sha384, id_sha512, kdf, setEngine, stringPrep, verifySCTsForCertificate, MLDSAPublicKey }; diff --git a/toolkit/locales/en-US/toolkit/about/certviewer.ftl b/toolkit/locales/en-US/toolkit/about/certviewer.ftl --- a/toolkit/locales/en-US/toolkit/about/certviewer.ftl +++ b/toolkit/locales/en-US/toolkit/about/certviewer.ftl @@ -45,20 +45,22 @@ certificate-viewer-organization = Organization certificate-viewer-organizational-unit = Organizational Unit certificate-viewer-policy = Policy certificate-viewer-protocol = Protocol certificate-viewer-public-value = Public Value +certificate-viewer-mldsa-public-value = Public Value certificate-viewer-purposes = Purposes certificate-viewer-qualifier = Qualifier certificate-viewer-qualifiers = Qualifiers certificate-viewer-required = Required certificate-viewer-unsupported = <unsupported> # Inc. means Incorporated, e.g GitHub is incorporated in Delaware certificate-viewer-inc-state-province = Inc. State/Province certificate-viewer-state-province = State/Province certificate-viewer-sha-1 = SHA-1 certificate-viewer-sha-256 = SHA-256 +certificate-viewer-security-level = Security Level certificate-viewer-serial-number = Serial Number certificate-viewer-signature-algorithm = Signature Algorithm certificate-viewer-signature-scheme = Signature Scheme certificate-viewer-timestamp = Timestamp certificate-viewer-value = Value