Skip to content

Commit

Permalink
feat: Add Noise Extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoPolo committed Sep 27, 2022
1 parent 77a95fd commit 5d12bf9
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 113 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@
"uint8arrays": "^3.1.0"
},
"devDependencies": {
"@libp2p/daemon-client": "2.0.4",
"@libp2p/daemon-server": "^3.0.0",
"@libp2p/daemon-client": "^3.0.1",
"@libp2p/daemon-server": "^3.0.1",
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.1",
"@libp2p/interop": "^2.1.0",
"@libp2p/interop": "^3.0.1",
"@libp2p/mplex": "^5.0.0",
"@libp2p/peer-id-factory": "^1.0.8",
"@libp2p/tcp": "^3.0.3",
"@multiformats/multiaddr": "^10.3.3",
"@multiformats/multiaddr": "^11.0.0",
"aegir": "^37.3.0",
"benchmark": "^2.1.4",
"execa": "^6.1.0",
Expand Down
4 changes: 3 additions & 1 deletion src/@types/handshake-interface.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { PeerId } from '@libp2p/interface-peer-id'
import type { bytes } from './basic.js'
import type { NoiseSession } from './handshake.js'
import type { NoiseExtensions } from '../proto/payload.js'

export interface IHandshake {
session: NoiseSession
remotePeer: PeerId
remoteEarlyData: bytes
remoteNoiseExtensions: NoiseExtensions
encrypt: (plaintext: bytes, session: NoiseSession) => bytes
decrypt: (ciphertext: bytes, session: NoiseSession) => {plaintext: bytes, valid: boolean}
decrypt: (ciphertext: bytes, session: NoiseSession) => { plaintext: bytes, valid: boolean }
}
10 changes: 10 additions & 0 deletions src/handshake-xx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import {
getPeerIdFromPayload,
verifySignedPayload
} from './utils.js'
import type { NoiseExtensions } from './proto/payload.js'

export class XXHandshake implements IHandshake {
public isInitiator: boolean
public session: NoiseSession
public remotePeer!: PeerId
public remoteEarlyData: bytes
public remoteNoiseExtensions: NoiseExtensions = { webtransportCerthashes: [] }

protected payload: bytes
protected connection: ProtobufStream
Expand Down Expand Up @@ -98,6 +100,7 @@ export class XXHandshake implements IHandshake {
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
this.setRemoteNoiseExtension(decodedPayload.extensions)
} catch (e) {
const err = e as Error
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
Expand Down Expand Up @@ -133,6 +136,7 @@ export class XXHandshake implements IHandshake {
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
this.setRemoteNoiseExtension(decodedPayload.extensions)
} catch (e) {
const err = e as Error
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
Expand Down Expand Up @@ -174,4 +178,10 @@ export class XXHandshake implements IHandshake {
this.remoteEarlyData = data
}
}

protected setRemoteNoiseExtension(e: NoiseExtensions | null | undefined): void {
if (e) {
this.remoteNoiseExtensions = e
}
}
}
6 changes: 5 additions & 1 deletion src/noise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export class Noise implements INoiseConnection {
return {
conn,
remoteEarlyData: handshake.remoteEarlyData,
// @ts-ignore this isn't part of the interface yet. Fix me when https://github.com/libp2p/js-libp2p-interfaces/issues/291 is fixed
remoteNoiseExtensions: handshake.remoteNoiseExtensions,
remotePeer: handshake.remotePeer
}
}
Expand Down Expand Up @@ -108,7 +110,9 @@ export class Noise implements INoiseConnection {
return {
conn,
remoteEarlyData: handshake.remoteEarlyData,
remotePeer: handshake.remotePeer
remotePeer: handshake.remotePeer,
// @ts-ignore this isn't part of the interface yet. Fix me when https://github.com/libp2p/js-libp2p-interfaces/issues/291 is fixed
remoteNoiseExtensions: handshake.remoteNoiseExtensions,
}
}

Expand Down
16 changes: 10 additions & 6 deletions src/proto/payload.proto
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
syntax = "proto3";
package pb;
syntax = "proto2";

message NoiseHandshakePayload {
bytes identity_key = 1;
bytes identity_sig = 2;
bytes data = 3;
message NoiseExtensions {
repeated bytes webtransport_certhashes = 1;
}

message NoiseHandshakePayload {
optional bytes identity_key = 1;
optional bytes identity_sig = 2;
optional bytes data = 3;
optional NoiseExtensions extensions = 4;
}
230 changes: 153 additions & 77 deletions src/proto/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,100 +5,176 @@ import { encodeMessage, decodeMessage, message } from 'protons-runtime'
import type { Uint8ArrayList } from 'uint8arraylist'
import type { Codec } from 'protons-runtime'

export namespace pb {
export interface NoiseHandshakePayload {
identityKey: Uint8Array
identitySig: Uint8Array
data: Uint8Array
}
export interface NoiseExtensions {
webtransportCerthashes: Uint8Array[]
}

export namespace NoiseHandshakePayload {
let _codec: Codec<NoiseHandshakePayload>
export namespace NoiseExtensions {
let _codec: Codec<NoiseExtensions>

export const codec = (): Codec<NoiseHandshakePayload> => {
if (_codec == null) {
_codec = message<NoiseHandshakePayload>((obj, writer, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
}
export const codec = (): Codec<NoiseExtensions> => {
if (_codec == null) {
_codec = message<NoiseExtensions>((obj, writer, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
}

if (obj.identityKey != null) {
if (obj.webtransportCerthashes != null) {
for (const value of obj.webtransportCerthashes) {
writer.uint32(10)
writer.bytes(obj.identityKey)
} else {
throw new Error('Protocol error: required field "identityKey" was not found in object')
writer.bytes(value)
}

if (obj.identitySig != null) {
writer.uint32(18)
writer.bytes(obj.identitySig)
} else {
throw new Error('Protocol error: required field "identitySig" was not found in object')
} else {
throw new Error('Protocol error: required field "webtransportCerthashes" was not found in object')
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
}
}, (reader, length) => {
const obj: any = {
webtransportCerthashes: []
}

const end = length == null ? reader.len : reader.pos + length

while (reader.pos < end) {
const tag = reader.uint32()

switch (tag >>> 3) {
case 1:
obj.webtransportCerthashes.push(reader.bytes())
break
default:
reader.skipType(tag & 7)
break
}
}

if (obj.data != null) {
writer.uint32(26)
writer.bytes(obj.data)
} else {
throw new Error('Protocol error: required field "data" was not found in object')
}
return obj
})
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
}
}, (reader, length) => {
const obj: any = {
identityKey: new Uint8Array(0),
identitySig: new Uint8Array(0),
data: new Uint8Array(0)
}
return _codec
}

const end = length == null ? reader.len : reader.pos + length

while (reader.pos < end) {
const tag = reader.uint32()

switch (tag >>> 3) {
case 1:
obj.identityKey = reader.bytes()
break
case 2:
obj.identitySig = reader.bytes()
break
case 3:
obj.data = reader.bytes()
break
default:
reader.skipType(tag & 7)
break
}
}
export const encode = (obj: NoiseExtensions): Uint8Array => {
return encodeMessage(obj, NoiseExtensions.codec())
}

if (obj.identityKey == null) {
throw new Error('Protocol error: value for required field "identityKey" was not found in protobuf')
}
export const decode = (buf: Uint8Array | Uint8ArrayList): NoiseExtensions => {
return decodeMessage(buf, NoiseExtensions.codec())
}
}

if (obj.identitySig == null) {
throw new Error('Protocol error: value for required field "identitySig" was not found in protobuf')
}
export interface NoiseHandshakePayload {
identityKey: Uint8Array
identitySig: Uint8Array
data: Uint8Array
extensions: NoiseExtensions
}

if (obj.data == null) {
throw new Error('Protocol error: value for required field "data" was not found in protobuf')
export namespace NoiseHandshakePayload {
let _codec: Codec<NoiseHandshakePayload>

export const codec = (): Codec<NoiseHandshakePayload> => {
if (_codec == null) {
_codec = message<NoiseHandshakePayload>((obj, writer, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
}

if (obj.identityKey != null) {
writer.uint32(10)
writer.bytes(obj.identityKey)
} else {
throw new Error('Protocol error: required field "identityKey" was not found in object')
}

if (obj.identitySig != null) {
writer.uint32(18)
writer.bytes(obj.identitySig)
} else {
throw new Error('Protocol error: required field "identitySig" was not found in object')
}

if (obj.data != null) {
writer.uint32(26)
writer.bytes(obj.data)
} else {
throw new Error('Protocol error: required field "data" was not found in object')
}

if (obj.extensions != null) {
writer.uint32(34)
NoiseExtensions.codec().encode(obj.extensions, writer)
} else {
throw new Error('Protocol error: required field "extensions" was not found in object')
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
}
}, (reader, length) => {
const obj: any = {
identityKey: new Uint8Array(0),
identitySig: new Uint8Array(0),
data: new Uint8Array(0),
extensions: undefined
}

const end = length == null ? reader.len : reader.pos + length

while (reader.pos < end) {
const tag = reader.uint32()

switch (tag >>> 3) {
case 1:
obj.identityKey = reader.bytes()
break
case 2:
obj.identitySig = reader.bytes()
break
case 3:
obj.data = reader.bytes()
break
case 4:
obj.extensions = NoiseExtensions.codec().decode(reader, reader.uint32())
break
default:
reader.skipType(tag & 7)
break
}
}

return obj
})
}
if (obj.identityKey == null) {
throw new Error('Protocol error: value for required field "identityKey" was not found in protobuf')
}

return _codec
}
if (obj.identitySig == null) {
throw new Error('Protocol error: value for required field "identitySig" was not found in protobuf')
}

export const encode = (obj: NoiseHandshakePayload): Uint8Array => {
return encodeMessage(obj, NoiseHandshakePayload.codec())
}
if (obj.data == null) {
throw new Error('Protocol error: value for required field "data" was not found in protobuf')
}

export const decode = (buf: Uint8Array | Uint8ArrayList): NoiseHandshakePayload => {
return decodeMessage(buf, NoiseHandshakePayload.codec())
if (obj.extensions == null) {
throw new Error('Protocol error: value for required field "extensions" was not found in protobuf')
}

return obj
})
}

return _codec
}

export const encode = (obj: NoiseHandshakePayload): Uint8Array => {
return encodeMessage(obj, NoiseHandshakePayload.codec())
}

export const decode = (buf: Uint8Array | Uint8ArrayList): NoiseHandshakePayload => {
return decodeMessage(buf, NoiseHandshakePayload.codec())
}
}
Loading

0 comments on commit 5d12bf9

Please sign in to comment.