Skip to content

Instantly share code, notes, and snippets.

@jawj
Created January 16, 2025 18:29
Show Gist options
  • Save jawj/04a90e51196ac054d6741c8d079d9cff to your computer and use it in GitHub Desktop.
Save jawj/04a90e51196ac054d6741c8d079d9cff to your computer and use it in GitHub Desktop.
Signature algorithm digest name from X.509 certificate
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