Implement SSHFP records (#80)

* Add SSHFP record support

Use offset since we may receive a full dns packet
Account for maximum hash lengths for each hash type
fix: don't use the output of `Buffer#copy()`, instead use `Buffer#byteLength`
fix: normalize fingerprint string
fix: the offset pointer starts out at RDLENGTH
fix: account for the `RDLENGTH` field in `rsshfp.decode()`

* tests: add test for the SSHFP record type
This commit is contained in:
wolfy1339 2022-06-14 17:09:01 -04:00 committed by GitHub
parent a5ee7897e2
commit fab30b2588
Signed by: Github
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 0 deletions

View File

@ -1294,6 +1294,65 @@ rds.encodingLength = function (digest) {
return 6 + Buffer.byteLength(digest.digest) return 6 + Buffer.byteLength(digest.digest)
} }
const rsshfp = exports.sshfp = {}
rsshfp.getFingerprintLengthForHashType = function getFingerprintLengthForHashType (hashType) {
switch (hashType) {
case 1: return 20
case 2: return 32
}
}
rsshfp.encode = function encode (record, buf, offset) {
if (!buf) buf = Buffer.alloc(rsshfp.encodingLength(record))
if (!offset) offset = 0
const oldOffset = offset
offset += 2 // The function call starts with the offset pointer at the RDLENGTH field, not the RDATA one
buf[offset] = record.algorithm
offset += 1
buf[offset] = record.hash
offset += 1
const fingerprintBuf = Buffer.from(record.fingerprint.toUpperCase(), 'hex')
if (fingerprintBuf.length !== rsshfp.getFingerprintLengthForHashType(record.hash)) {
throw new Error('Invalid fingerprint length')
}
fingerprintBuf.copy(buf, offset)
offset += fingerprintBuf.byteLength
rsshfp.encode.bytes = offset - oldOffset
buf.writeUInt16BE(rsshfp.encode.bytes - 2, oldOffset)
return buf
}
rsshfp.encode.bytes = 0
rsshfp.decode = function decode (buf, offset) {
if (!offset) offset = 0
const oldOffset = offset
const record = {}
offset += 2 // Account for the RDLENGTH field
record.algorithm = buf[offset]
offset += 1
record.hash = buf[offset]
offset += 1
const fingerprintLength = rsshfp.getFingerprintLengthForHashType(record.hash)
record.fingerprint = buf.slice(offset, offset + fingerprintLength).toString('hex').toUpperCase()
offset += fingerprintLength
rsshfp.decode.bytes = offset - oldOffset
return record
}
rsshfp.decode.bytes = 0
rsshfp.encodingLength = function (record) {
return 4 + Buffer.from(record.fingerprint, 'hex').byteLength
}
const renc = exports.record = function (type) { const renc = exports.record = function (type) {
switch (type.toUpperCase()) { switch (type.toUpperCase()) {
case 'A': return ra case 'A': return ra
@ -1315,6 +1374,7 @@ const renc = exports.record = function (type) {
case 'RP': return rrp case 'RP': return rrp
case 'NSEC': return rnsec case 'NSEC': return rnsec
case 'NSEC3': return rnsec3 case 'NSEC3': return rnsec3
case 'SSHFP': return rsshfp
case 'DS': return rds case 'DS': return rds
} }
return runknown return runknown

View File

@ -107,6 +107,15 @@ tape('soa', function (t) {
t.end() t.end()
}) })
tape('sshfp', function (t) {
testEncoder(t, packet.sshfp, {
algorithm: 1,
hash: 1,
fingerprint: 'A108C9F834354D5B37AF988141C9294822F5BC00'
})
t.end()
})
tape('a', function (t) { tape('a', function (t) {
testEncoder(t, packet.a, '127.0.0.1') testEncoder(t, packet.a, '127.0.0.1')
t.end() t.end()