Skip to content

Commit

Permalink
feat: add support for webrtc and certhash (#261) (#262)
Browse files Browse the repository at this point in the history
For use with the js implementation of the webrtc transport

The most relevant point of info, from [the draft spec](https://github.com/libp2p/specs/blob/4b8e890c49061eb62c9d16a9afd3aa0a738e6285/webrtc/README.md#addressing): 

> The TLS certificate fingerprint in /certhash is a [multibase](https://github.com/multiformats/multibase) encoded [multihash](https://github.com/multiformats/multihash).
  • Loading branch information
John-LittleBearLabs authored Aug 30, 2022
1 parent bcb0503 commit e5a8e27
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getProtocol } from './protocols-table.js'
import { CID } from 'multiformats/cid'
import { base32 } from 'multiformats/bases/base32'
import { base58btc } from 'multiformats/bases/base58'
import { bases } from 'multiformats/basics'
import * as Digest from 'multiformats/hashes/digest'
import varint from 'varint'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
Expand Down Expand Up @@ -51,6 +52,8 @@ export function convertToString (proto: number | string, buf: Uint8Array) {
return bytes2onion(buf)
case 445: // onion3
return bytes2onion(buf)
case 466: // certhash
return bytes2mb(buf)
default:
return uint8ArrayToString(buf, 'base16') // no clue. convert to hex
}
Expand Down Expand Up @@ -84,11 +87,20 @@ export function convertToBytes (proto: string | number, str: string) {
return onion2bytes(str)
case 445: // onion3
return onion32bytes(str)
case 466: // certhash
return mb2bytes(str)
default:
return uint8ArrayFromString(str, 'base16') // no clue. convert from hex
}
}

const decoders = Object.values(bases).map((c) => c.decoder)
const anybaseDecoder = (function () {
let acc = decoders[0].or(decoders[1])
decoders.slice(2).forEach((d) => (acc = acc.or(d)))
return acc
})()

function ip2bytes (ipString: string) {
if (!ip.isIP(ipString)) {
throw new Error('invalid ip address')
Expand Down Expand Up @@ -148,6 +160,22 @@ function mh2bytes (hash: string) {
return uint8ArrayConcat([size, mh], size.length + mh.length)
}

function mb2bytes (mbstr: string) {
const mb = anybaseDecoder.decode(mbstr)
const size = Uint8Array.from(varint.encode(mb.length))
return uint8ArrayConcat([size, mb], size.length + mb.length)
}
function bytes2mb (buf: Uint8Array) {
const size = varint.decode(buf)
const hash = buf.slice(varint.decode.bytes)

if (hash.length !== size) {
throw new Error('inconsistent lengths')
}

return 'u' + uint8ArrayToString(hash, 'base64url')
}

/**
* Converts bytes to bas58btc string
*/
Expand Down
2 changes: 2 additions & 0 deletions src/protocols-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const table: Array<[number, number, string, boolean?, boolean?]> = [
[275, 0, 'p2p-webrtc-star'],
[276, 0, 'p2p-webrtc-direct'],
[277, 0, 'p2p-stardust'],
[280, 0, 'webrtc'],
[290, 0, 'p2p-circuit'],
[301, 0, 'udt'],
[302, 0, 'utp'],
Expand All @@ -40,6 +41,7 @@ export const table: Array<[number, number, string, boolean?, boolean?]> = [
[445, 296, 'onion3'],
[446, V, 'garlic64'],
[460, 0, 'quic'],
[466, V, 'certhash'],
[477, 0, 'ws'],
[478, 0, 'wss'],
[479, 0, 'p2p-websocket-star'],
Expand Down
13 changes: 13 additions & 0 deletions test/convert.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,17 @@ describe('convert', () => {
expect(convert.convertToString('sctp', buffer.subarray(5))).to.equal('1234')
})
})

it('can round-trip certhash, though encoding base may change', () => {
const myCertFingerprint = {
algorithm: 'sha-256',
value: 'f4:32:a0:45:34:62:85:e0:d8:d7:75:36:84:72:8e:b2:aa:9e:71:64:e4:eb:fe:06:51:64:42:64:fe:04:a8:d0'
}
const mb = 'f' + myCertFingerprint.value.replaceAll(':', '')
const bytes = convert.convertToBytes('certhash', mb)
const outcome = convert.convertToString(466, bytes)
expect(outcome).to.equal('u9DKgRTRiheDY13U2hHKOsqqecWTk6_4GUWRCZP4EqNA')
const bytesOut = convert.convertToBytes(466, outcome)
expect(bytesOut.toString()).to.equal(bytes.toString())
})
})

0 comments on commit e5a8e27

Please sign in to comment.