From e5a8e27fb526e3a58af5ebe8873f6cc67b9d4483 Mon Sep 17 00:00:00 2001 From: John Turpish <97759690+John-LittleBearLabs@users.noreply.github.com> Date: Tue, 30 Aug 2022 13:12:49 -0400 Subject: [PATCH] feat: add support for webrtc and certhash (#261) (#262) 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). --- src/convert.ts | 28 ++++++++++++++++++++++++++++ src/protocols-table.ts | 2 ++ test/convert.spec.ts | 13 +++++++++++++ 3 files changed, 43 insertions(+) diff --git a/src/convert.ts b/src/convert.ts index e35613d2..c38a64b0 100644 --- a/src/convert.ts +++ b/src/convert.ts @@ -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' @@ -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 } @@ -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') @@ -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 */ diff --git a/src/protocols-table.ts b/src/protocols-table.ts index 0e839cc9..657003c6 100644 --- a/src/protocols-table.ts +++ b/src/protocols-table.ts @@ -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'], @@ -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'], diff --git a/test/convert.spec.ts b/test/convert.spec.ts index 15bf40cd..5d395c1e 100644 --- a/test/convert.spec.ts +++ b/test/convert.spec.ts @@ -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()) + }) })