Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
@libp2p/webrtc
Advanced tools
A libp2p transport using WebRTC connections
A libp2p transport based on WebRTC datachannels.
WebRTC is a specification that allows real-time communication between nodes - it's commonly used in browser video conferencing applications but it also provides a reliable data transport mechanism called data channels which libp2p uses to facilitate protocol streams between peers.
There are two transports exposed by this module, webRTC and webRTCDirect.
The connection establishment phase of WebRTC involves a handshake using SDP during which two peers will exchange information such as open ports, network addresses and required capabilities.
A third party is usually necessary to carry out this handshake, forwarding messages between the two peers until they can make a direct connection between themselves.
The WebRTC transport uses libp2p Circuit Relays to forward SDP messages. Once a direct connection is formed the relay plays no further part in the exchange.
WebRTC Direct uses a technique known as SDP munging to skip the handshake step, instead encoding enough information in the connection request that the responder can derive what would have been in the handshake messages and so requires no third parties to establish a connection.
A WebRTC Direct multiaddr also includes a certhash of the target peer - this is used to allow opening a connection to the remote, which would otherwise be denied due to use of a self-signed certificate.
In both cases, once the connection is established a Noise handshake is carried out to ensure that the remote peer has the private key that corresponds to the public key that makes up their PeerId, giving you both encryption and authentication.
WebRTC is supported in both Node.js and browsers.
At the time of writing, WebRTC Direct is dial-only in browsers and not supported in Node.js at all.
Support in Node.js is possible but PRs will need to be opened to libdatachannel and the appropriate APIs exposed in node-datachannel.
WebRTC Direct support is available in rust-libp2p and arriving soon in go-libp2p.
See the WebRTC section of https://connectivity.libp2p.io for more information.
WebRTC requires use of a relay to connect two nodes. The listener first discovers a relay server and makes a reservation, then the dialer can connect via the relayed address.
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { echo } from '@libp2p/echo'
import { circuitRelayTransport, circuitRelayServer } from '@libp2p/circuit-relay-v2'
import { identify } from '@libp2p/identify'
import { webRTC } from '@libp2p/webrtc'
import { webSockets } from '@libp2p/websockets'
import * as filters from '@libp2p/websockets/filters'
import { WebRTC } from '@multiformats/multiaddr-matcher'
import delay from 'delay'
import { pipe } from 'it-pipe'
import { createLibp2p } from 'libp2p'
import type { Multiaddr } from '@multiformats/multiaddr'
// the relay server listens on a transport dialable by the listener and the
// dialer, and has a relay service configured
const relay = await createLibp2p({
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0/ws']
},
transports: [
webSockets({filter: filters.all})
],
connectionEncrypters: [noise()],
streamMuxers: [yamux()],
services: {
identify: identify(),
relay: circuitRelayServer()
}
})
// the listener has a WebSocket transport to dial the relay, a Circuit Relay
// transport to make a reservation, and a WebRTC transport to accept incoming
// WebRTC connections
const listener = await createLibp2p({
addresses: {
listen: [
'/p2p-circuit',
'/webrtc'
]
},
transports: [
webSockets({filter: filters.all}),
webRTC(),
circuitRelayTransport()
],
connectionEncrypters: [noise()],
streamMuxers: [yamux()],
services: {
identify: identify(),
echo: echo()
}
})
// the listener dials the relay (or discovers a public relay via some other
// method)
await listener.dial(relay.getMultiaddrs(), {
signal: AbortSignal.timeout(5000)
})
let webRTCMultiaddr: Multiaddr | undefined
// wait for the listener to make a reservation on the relay
while (true) {
webRTCMultiaddr = listener.getMultiaddrs().find(ma => WebRTC.matches(ma))
if (webRTCMultiaddr != null) {
break
}
// try again later
await delay(1000)
}
// the dialer has Circuit Relay, WebSocket and WebRTC transports to dial
// the listener via the relay, complete the SDP handshake and establish a
// direct WebRTC connection
const dialer = await createLibp2p({
transports: [
webSockets({filter: filters.all}),
webRTC(),
circuitRelayTransport()
],
connectionEncrypters: [noise()],
streamMuxers: [yamux()],
services: {
identify: identify(),
echo: echo()
}
})
// dial the listener and open an echo protocol stream
const stream = await dialer.dialProtocol(webRTCMultiaddr, dialer.services.echo.protocol, {
signal: AbortSignal.timeout(5000)
})
// we can now stop the relay
await relay.stop()
// send/receive some data from the remote peer via a direct connection
await pipe(
[new TextEncoder().encode('hello world')],
stream,
async source => {
for await (const buf of source) {
console.info(new TextDecoder().decode(buf.subarray()))
}
}
)
At the time of writing WebRTC Direct is dial-only in browsers and unsupported in Node.js.
The only implementation that supports a WebRTC Direct listener is go-libp2p and it's not yet enabled by default.
import { createLibp2p } from 'libp2p'
import { noise } from '@chainsafe/libp2p-noise'
import { multiaddr } from '@multiformats/multiaddr'
import { pipe } from 'it-pipe'
import { fromString, toString } from 'uint8arrays'
import { webRTCDirect } from '@libp2p/webrtc'
const node = await createLibp2p({
transports: [
webRTCDirect()
],
connectionEncrypters: [
noise()
]
})
await node.start()
// this multiaddr corresponds to a remote node running a WebRTC Direct listener
const ma = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc-direct/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ')
const stream = await node.dialProtocol(ma, '/my-protocol/1.0.0', {
signal: AbortSignal.timeout(10_000)
})
await pipe(
[fromString(`Hello js-libp2p-webrtc\n`)],
stream,
async function (source) {
for await (const buf of source) {
console.info(toString(buf.subarray()))
}
}
)
$ npm i @libp2p/webrtc
<script>
tagLoading this module through a script tag will make its exports available as Libp2pWebrtc
in the global namespace.
<script src="https://unpkg.com/@libp2p/webrtc/dist/index.min.js"></script>
Licensed under either of
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
FAQs
A libp2p transport using WebRTC connections
The npm package @libp2p/webrtc receives a total of 6,502 weekly downloads. As such, @libp2p/webrtc popularity was classified as popular.
We found that @libp2p/webrtc demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.