Skip to content
This repository has been archived by the owner on Jun 19, 2023. It is now read-only.

Commit

Permalink
fix: Fetch local fingerprint from SDP (#109)
Browse files Browse the repository at this point in the history
* fix: Fetch local fingerprint from SDP

Since firefox does not support `getFingerprints`, we fetch the local
fingerprint from the PeerConnection's `localDescription` field by
parsing the fingerprint line from the SDP.

* remove erroneous comment

* update sdp

* working browser-to-server in firefox

* test firefox browser-to-server in CI

* remove hack

* address review comment

* Review nits

* Rename test file for playwright

* Build once for browser tests

* Move code back to make diff smaller

---------

Co-authored-by: Marco Munizaga <git@marcopolo.io>
  • Loading branch information
ckousik and MarcoPolo committed May 3, 2023
1 parent 2f8faa3 commit 3673d6c
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 27 deletions.
2 changes: 1 addition & 1 deletion examples/browser-to-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"relay": "node relay.js",
"test:firefox": "npm run build && playwright test --browser=firefox tests",
"test:chrome": "npm run build && playwright test tests",
"test": "npm run test:firefox && npm run test:chrome"
"test": "npm run build && playwright test tests && playwright test --browser firefox tests"
},
"dependencies": {
"@chainsafe/libp2p-noise": "^11.0.0",
Expand Down
4 changes: 3 additions & 1 deletion examples/browser-to-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"start": "vite",
"build": "vite build",
"go-libp2p-server": "cd ../go-libp2p-server && go run ./main.go",
"test": "npm run build && playwright test tests"
"test:chrome": "npm run build && playwright test tests",
"test:firefox": "npm run build && playwright test --browser firefox tests",
"test": "npm run build && playwright test tests && playwright test --browser firefox tests"
},
"dependencies": {
"@chainsafe/libp2p-noise": "^11.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,9 @@ play.describe('bundle ipfs with parceljs:', () => {
// Received message '${message}'
const connections = await page.textContent(output)


expect(connections).toContain(`Dialing '${serverAddr}'`)
expect(connections).toContain(`Peer connected '${serverAddr}'`)



expect(connections).toContain(`Sending message '${message}'`)
expect(connections).toContain(`Received message '${message}'`)
})
Expand Down
31 changes: 31 additions & 0 deletions src/sdp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,37 @@ const log = logger('libp2p:webrtc:sdp')
// @ts-expect-error - Not easy to combine these types.
export const mbdecoder: any = Object.values(bases).map(b => b.decoder).reduce((d, b) => d.or(b))

export function getLocalFingerprint (pc: RTCPeerConnection): string | undefined {
// try to fetch fingerprint from local certificate
const localCert = pc.getConfiguration().certificates?.at(0)
if (localCert == null || localCert.getFingerprints == null) {
log.trace('fetching fingerprint from local SDP')
const localDescription = pc.localDescription
if (localDescription == null) {
return undefined
}
return getFingerprintFromSdp(localDescription.sdp)
}

log.trace('fetching fingerprint from local certificate')

if (localCert.getFingerprints().length === 0) {
return undefined
}

const fingerprint = localCert.getFingerprints()[0].value
if (fingerprint == null) {
throw invalidFingerprint('', 'no fingerprint on local certificate')
}

return fingerprint
}

const fingerprintRegex = /^a=fingerprint:(?:\w+-[0-9]+)\s(?<fingerprint>(:?[0-9a-fA-F]{2})+)$/m
export function getFingerprintFromSdp (sdp: string): string | undefined {
const searchResult = sdp.match(fingerprintRegex)
return searchResult?.groups?.fingerprint
}
/**
* Get base2 | identity decoders
*/
Expand Down
1 change: 1 addition & 0 deletions src/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export class WebRTCStream implements Stream {

constructor (opts: StreamInitOpts) {
this.channel = opts.channel
this.channel.binaryType = 'arraybuffer'
this.id = this.channel.label

this.stat = opts.stat
Expand Down
40 changes: 18 additions & 22 deletions src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export class WebRTCDirectTransport implements Transport {
namedCurve: 'P-256',
hash: sdp.toSupportedHashFunction(remoteCerthash.name)
} as any)

const peerConnection = new RTCPeerConnection({ certificates: [certificate] })

// create data channel for running the noise handshake. Once the data channel is opened,
Expand Down Expand Up @@ -181,12 +182,24 @@ export class WebRTCDirectTransport implements Transport {
source: {
[Symbol.asyncIterator]: async function * () {
for await (const list of wrappedChannel.source) {
yield list.subarray()
for (const buf of list) {
yield buf
}
}
}
}
}

// Creating the connection before completion of the noise
// handshake ensures that the stream opening callback is set up
const maConn = new WebRTCMultiaddrConnection({
peerConnection,
remoteAddr: ma,
timeline: {
open: Date.now()
}
})

const eventListeningName = isFirefox ? 'iceconnectionstatechange' : 'connectionstatechange'

peerConnection.addEventListener(eventListeningName, () => {
Expand All @@ -206,16 +219,6 @@ export class WebRTCDirectTransport implements Transport {
}
}, { signal })

// Creating the connection before completion of the noise
// handshake ensures that the stream opening callback is set up
const maConn = new WebRTCMultiaddrConnection({
peerConnection,
remoteAddr: ma,
timeline: {
open: Date.now()
}
})

const muxerFactory = new DataChannelMuxerFactory(peerConnection)

// For outbound connections, the remote is expected to start the noise handshake.
Expand All @@ -234,19 +237,12 @@ export class WebRTCDirectTransport implements Transport {
throw invalidArgument('no local certificate')
}

const localCert = pc.getConfiguration().certificates?.at(0)

if (localCert === undefined || localCert.getFingerprints().length === 0) {
throw invalidArgument('no fingerprint on local certificate')
}

const localFingerprint = localCert.getFingerprints()[0]

if (localFingerprint.value === undefined) {
throw invalidArgument('no fingerprint on local certificate')
const localFingerprint = sdp.getLocalFingerprint(pc)
if (localFingerprint == null) {
throw invalidArgument('no local fingerprint found')
}

const localFpString = localFingerprint.value.replace(/:/g, '')
const localFpString = localFingerprint.trim().toLowerCase().replaceAll(':', '')
const localFpArray = uint8arrayFromString(localFpString, 'hex')
const local = multihashes.encode(localFpArray, hashCode)
const remote: Uint8Array = sdp.mbdecoder.decode(sdp.certhash(ma))
Expand Down
5 changes: 5 additions & 0 deletions test/sdp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ describe('SDP', () => {
])
})

it('extracts a fingerprint from sdp', () => {
const fingerprint = underTest.getFingerprintFromSdp(sampleSdp)
expect(fingerprint).to.eq('72:68:47:CD:48:B0:5E:C5:60:4D:15:9C:BF:40:1D:6F:00:A1:23:EC:90:17:0E:2C:D1:B3:8F:D2:9D:37:E5:B1')
})

it('munges the ufrag and pwd in a SDP', () => {
const result = underTest.munge({ type: 'answer', sdp: sampleSdp }, 'someotheruserfragmentstring')
const expected = `v=0
Expand Down

0 comments on commit 3673d6c

Please sign in to comment.