Skip to content

Commit

Permalink
Merge pull request libp2p#55 from little-bear-labs/ckousik/hash-function
Browse files Browse the repository at this point in the history
Determine hash function from multiaddr
  • Loading branch information
ckousik committed Nov 3, 2022
2 parents 9172498 + 9854ff3 commit 2e8da7c
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 34 deletions.
45 changes: 24 additions & 21 deletions src/sdp.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { inappropriateMultiaddr, invalidArgument, unsupportedHashAlgorithm } from './error.js';
import { logger } from '@libp2p/logger';
import { Multiaddr } from '@multiformats/multiaddr';
import {inappropriateMultiaddr, invalidArgument, unsupportedHashAlgorithm} from './error.js';
import {logger} from '@libp2p/logger';
import {Multiaddr} from '@multiformats/multiaddr';
import * as multihashes from 'multihashes';
import { bases } from 'multiformats/basics';
import {bases} from 'multiformats/basics';

const log = logger('libp2p:webrtc:sdp');

Expand Down Expand Up @@ -41,32 +41,35 @@ export function certhash(ma: Multiaddr): string {
}
}

export function decodeCerthash(certhash: string) {
const mbdecoded = mbdecoder.decode(certhash);
return multihashes.decode(mbdecoded);
}

export function certhashToFingerprint(ma: Multiaddr): string[] {
const certhash_value = certhash(ma);
// certhash_value is a multibase encoded multihash encoded string
const mbdecoded = mbdecoder.decode(certhash_value);
const mhdecoded = multihashes.decode(mbdecoded);
let prefix = '';
switch (mhdecoded.name) {
case 'md5':
prefix = 'md5';
break;
case 'sha2-256':
prefix = 'sha-256';
break;
case 'sha2-512':
prefix = 'sha-512';
break;
default:
throw unsupportedHashAlgorithm(mhdecoded.name);
}
const mhdecoded = decodeCerthash(certhash(ma));
let prefix = toSupportedHashFunction(mhdecoded.name);

const fp = mhdecoded.digest.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
const fpSdp = fp.match(/.{1,2}/g)!.join(':');

return [`${prefix.toUpperCase()} ${fpSdp.toUpperCase()}`, fp];
}

export function toSupportedHashFunction(name: multihashes.HashName): string {
switch (name) {
case 'sha1':
return 'sha-1'
case 'sha2-256':
return 'sha-256';
case 'sha2-512':
return 'sha-512';
default:
throw unsupportedHashAlgorithm(name);
}
}

function ma2sdp(ma: Multiaddr, ufrag: string): string {
const IP = ip(ma);
const IPVERSION = ipv(ma);
Expand Down
24 changes: 11 additions & 13 deletions src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import defer from 'p-defer';
import {fromString as uint8arrayFromString} from 'uint8arrays/from-string';
import {concat} from 'uint8arrays/concat';
import * as multihashes from 'multihashes';
import {dataChannelError, inappropriateMultiaddr, unimplemented, invalidArgument, unsupportedHashAlgorithm} from './error.js';
import {dataChannelError, inappropriateMultiaddr, unimplemented, invalidArgument} from './error.js';
import {WebRTCMultiaddrConnection} from './maconn.js';
import {DataChannelMuxerFactory} from './muxer.js';

const log = logger('libp2p:webrtc:transport');
const HANDSHAKE_TIMEOUT_MS = 10000;
const WEBRTC_CODE: number = 280;
const CERTHASH_CODE: number = 466;


export interface WebRTCTransportComponents {
peerId: PeerId
Expand Down Expand Up @@ -59,14 +62,15 @@ export class WebRTCTransport implements Transport {
throw inappropriateMultiaddr("we need to have the remote's PeerId");
}

const remoteCerthash = sdp.decodeCerthash(sdp.certhash(ma))
// ECDSA is preferred over RSA here. From our testing we find that P-256 elliptic
// curve is supported by Pion, webrtc-rs, as well as Chromium (P-228 and P-384
// was not supported in Chromium). We fix the hash algorith to SHA-256 for
// reasons documented here: https://github.com/libp2p/specs/pull/412#discussion_r968327480
// was not supported in Chromium). We use the same hash function as found in the
// multiaddr if it is supported.
const certificate = await RTCPeerConnection.generateCertificate({
name: 'ECDSA',
namedCurve: 'P-256',
hash: 'SHA-256',
hash: sdp.toSupportedHashFunction(remoteCerthash.name),
} as any);
const peerConnection = new RTCPeerConnection({certificates: [certificate]});

Expand Down Expand Up @@ -112,7 +116,7 @@ export class WebRTCTransport implements Transport {
// do noise handshake
//set the Noise Prologue to libp2p-webrtc-noise:<FINGERPRINTS> before starting the actual Noise handshake.
// <FINGERPRINTS> is the concatenation of the of the two TLS fingerprints of A and B in their multihash byte representation, sorted in ascending order.
const fingerprintsPrologue = this.generateNoisePrologue(peerConnection, ma);
const fingerprintsPrologue = this.generateNoisePrologue(peerConnection, remoteCerthash.name, ma);
// Since we use the default crypto interface and do not use a static key or early data,
// we pass in undefined for these parameters.
const noise = new Noise(undefined, undefined, undefined, fingerprintsPrologue);
Expand Down Expand Up @@ -146,7 +150,7 @@ export class WebRTCTransport implements Transport {
return upgraded
}

private generateNoisePrologue(pc: RTCPeerConnection, ma: Multiaddr): Uint8Array {
private generateNoisePrologue(pc: RTCPeerConnection, hashName: multihashes.HashName, ma: Multiaddr): Uint8Array {
if (pc.getConfiguration().certificates?.length === 0) {
throw invalidArgument('no local certificate');
}
Expand All @@ -158,10 +162,7 @@ export class WebRTCTransport implements Transport {
const localFingerprint = localCert.getFingerprints()[0];
const localFpString = localFingerprint.value!.replaceAll(':', '');
const localFpArray = uint8arrayFromString(localFpString, 'hex');
if (localFingerprint.algorithm! != 'sha-256') {
throw unsupportedHashAlgorithm(localFingerprint.algorithm || 'none');
}
const local = multihashes.encode(localFpArray, multihashes.names['sha2-256']);
const local = multihashes.encode(localFpArray, multihashes.names[hashName]);

const remote: Uint8Array = sdp.mbdecoder.decode(sdp.certhash(ma));
const prefix = uint8arrayFromString('libp2p-webrtc-noise:');
Expand All @@ -171,9 +172,6 @@ export class WebRTCTransport implements Transport {
}
}

const WEBRTC_CODE: number = 280;
const CERTHASH_CODE: number = 466;

function validMa(ma: Multiaddr): boolean {
const codes = ma.protoCodes();
return codes.includes(WEBRTC_CODE) && codes.includes(CERTHASH_CODE) && ma.getPeerId() != null;
Expand Down

0 comments on commit 2e8da7c

Please sign in to comment.