Created
January 16, 2025 18:29
-
-
Save jawj/04a90e51196ac054d6741c8d079d9cff to your computer and use it in GitHub Desktop.
Signature algorithm digest name from X.509 certificate
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
function x509Error() { | |
throw new Error('Could not extract signature algorithm digest from server certificate to perform channel binding') | |
} | |
function readASN1Length(data, index) { | |
let length = data[index++] | |
if (length < 0x80) return { length, index } | |
const lengthBytes = length & 0x7f | |
if (lengthBytes > 4) x509Error() | |
length = 0 | |
for (let i = 0; i < lengthBytes; i++) { | |
length = (length << 8) | data[index++] | |
} | |
return { length, index } | |
} | |
function readASN1OID(data, index) { | |
if (data[index++] !== 0x6) x509Error() // 6 = OID | |
const { length: OIDLength, index: indexAfterOIDLength } = readASN1Length(data, index) | |
index = indexAfterOIDLength | |
lastIndex = index + OIDLength | |
const byte1 = data[index++] | |
let oid = ((byte1 / 40) >> 0) + '.' + (byte1 % 40) | |
while (index < lastIndex) { | |
// loop over numbers in OID | |
let value = 0 | |
while (index < lastIndex) { | |
// loop over bytes in number | |
const nextByte = data[index++] | |
value = (value << 7) | (nextByte & 0x7f) | |
if (nextByte < 0x80) break | |
} | |
oid += '.' + value | |
} | |
return { oid, index } | |
} | |
function readASN1Seq(data, index) { | |
if (data[index++] !== 0x30) x509Error() // 30 = Sequence | |
return readASN1Length(data, index) | |
} | |
function signatureAlgorithmDigestFromCertificate(data, index) { | |
index = readASN1Seq(data, index).index | |
const { length: certInfoLength, index: indexAfterCertInfoLength } = readASN1Seq(data, index) | |
index = indexAfterCertInfoLength + certInfoLength // skip over certificate info | |
index = readASN1Seq(data, index).index // skip over signature length field | |
const { oid, index: indexAfterOID } = readASN1OID(data, index) | |
let digest | |
if (oid === '1.2.840.113549.1.1.10') { | |
// this is RSASSA-PSS, which specifies the digest in separate parameters | |
index = indexAfterOID | |
index = readASN1Seq(data, index).index | |
if (data[index++] !== 0xa0) x509Error() // a0 = constructed tag 0 | |
index = readASN1Length(data, index).index // skip over tag length field | |
index = readASN1Seq(data, index).index // skip over sequence length field | |
const { oid: hashOID } = readASN1OID(data, index) | |
digest = { | |
// plain hash OIDs | |
'1.2.840.113549.2.5': 'SHA-256', // upgraded from MD5 | |
'1.3.14.3.2.26': 'SHA-256', // upgraded from SHA-1 | |
'2.16.840.1.101.3.4.2.1': 'SHA-256', | |
'2.16.840.1.101.3.4.2.2': 'SHA-384', | |
'2.16.840.1.101.3.4.2.3': 'SHA-512', | |
}[hashOID] | |
} else { | |
digest = { | |
// ECDSA OIDs | |
'1.2.840.10045.4.1': 'SHA-256', // upgraded from SHA-1 | |
'1.2.840.10045.4.3.1': 'SHA-224', | |
'1.2.840.10045.4.3.2': 'SHA-256', | |
'1.2.840.10045.4.3.3': 'SHA-384', | |
'1.2.840.10045.4.3.4': 'SHA-512', | |
// RSA OIDs | |
'1.2.840.113549.1.1.4': 'SHA-256', // upgraded from MD5 | |
'1.2.840.113549.1.1.5': 'SHA-256', // upgraded from SHA-1 | |
'1.2.840.113549.1.1.11': 'SHA-256', | |
'1.2.840.113549.1.1.12': 'SHA-384', | |
'1.2.840.113549.1.1.13': 'SHA-512', | |
'1.2.840.113549.1.1.14': 'SHA-224', | |
'1.2.840.113549.1.1.15': 'SHA512-224', | |
'1.2.840.113549.1.1.16': 'SHA512-256', | |
}[oid] | |
} | |
if (!digest) x509Error() | |
return digest | |
} | |
module.exports = { signatureAlgorithmDigestFromCertificate } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment