Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,31 +71,29 @@
"@libp2p/interface-keys": "^1.0.2",
"@libp2p/interface-peer-id": "^1.0.2",
"@libp2p/logger": "^2.0.0",
"@libp2p/peer-collections": "^1.0.0",
"@libp2p/peer-collections": "^2.0.0",
"@libp2p/peer-id": "^1.1.8",
"@stablelib/chacha20poly1305": "^1.0.1",
"@stablelib/hkdf": "^1.0.1",
"@stablelib/sha256": "^1.0.1",
"@stablelib/x25519": "^1.0.1",
"it-length-prefixed": "^7.0.1",
"it-length-prefixed": "^8.0.2",
"it-pair": "^2.0.2",
"it-pb-stream": "^1.0.2",
"it-pb-stream": "^2.0.1",
"it-pipe": "^2.0.3",
"protons-runtime": "^1.0.3",
"uint8arraylist": "^1.4.0",
"it-stream-types": "^1.0.4",
"protons-runtime": "^2.0.1",
"uint8arraylist": "^2.0.0",
"uint8arrays": "^3.0.0"
},
"devDependencies": {
"@libp2p/interface-connection-encrypter-compliance-tests": "^1.0.1",
"@libp2p/peer-id-factory": "^1.0.8",
"aegir": "^37.3.0",
"benchmark": "^2.1.4",
"events": "^3.3.0",
"microtime": "^3.0.0",
"mkdirp": "^1.0.4",
"protons": "^3.0.3",
"sinon": "^14.0.0",
"util": "^0.12.4"
"protons": "^4.0.0",
"sinon": "^14.0.0"
},
"browser": {
"./dist/src/alloc-unsafe.js": "./dist/src/alloc-unsafe-browser.js",
Expand Down
6 changes: 3 additions & 3 deletions src/crypto/stablelib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export const stablelib: ICryptoInterface = {
const okmU8Array = hkdf.expand(96)
const okm = okmU8Array

const k1 = okm.slice(0, 32)
const k2 = okm.slice(32, 64)
const k3 = okm.slice(64, 96)
const k1 = okm.subarray(0, 32)
const k2 = okm.subarray(32, 64)
const k3 = okm.subarray(64, 96)

return [k1, k2, k3]
},
Expand Down
7 changes: 4 additions & 3 deletions src/crypto/streaming.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Transform } from 'it-stream-types'
import type { Uint8ArrayList } from 'uint8arraylist'
import type { IHandshake } from '../@types/handshake-interface.js'
import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from '../constants.js'

Expand All @@ -12,15 +13,15 @@ export function encryptStream (handshake: IHandshake): Transform<Uint8Array> {
end = chunk.length
}

const data = handshake.encrypt(chunk.slice(i, end), handshake.session)
const data = handshake.encrypt(chunk.subarray(i, end), handshake.session)
yield data
}
}
}
}

// Decrypt received payload to the user
export function decryptStream (handshake: IHandshake): Transform<Uint8Array> {
export function decryptStream (handshake: IHandshake): Transform<Uint8Array|Uint8ArrayList> {
return async function * (source) {
for await (const chunk of source) {
for (let i = 0; i < chunk.length; i += NOISE_MSG_MAX_LENGTH_BYTES) {
Expand All @@ -29,7 +30,7 @@ export function decryptStream (handshake: IHandshake): Transform<Uint8Array> {
end = chunk.length
}

const { plaintext: decrypted, valid } = await handshake.decrypt(chunk.slice(i, end), handshake.session)
const { plaintext: decrypted, valid } = await handshake.decrypt(chunk.subarray(i, end), handshake.session)
if (!valid) {
throw new Error('Failed to validate decrypted chunk')
}
Expand Down
23 changes: 12 additions & 11 deletions src/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
import type { Uint8ArrayList } from 'uint8arraylist'
import type { bytes } from './@types/basic.js'
import type { MessageBuffer } from './@types/handshake.js'
import type { LengthDecoderFunction, LengthEncoderFunction } from 'it-length-prefixed'

const allocUnsafe = (len: number): Uint8Array => {
if (globalThis.Buffer) {
Expand All @@ -11,14 +12,14 @@ const allocUnsafe = (len: number): Uint8Array => {
return new Uint8Array(len)
}

export const uint16BEEncode = (value: number, target?: Uint8Array, offset?: number): Uint8Array => {
target = target ?? allocUnsafe(2)
new DataView(target.buffer, target.byteOffset, target.byteLength).setUint16(offset ?? 0, value, false)
export const uint16BEEncode: LengthEncoderFunction = (value: number) => {
const target = allocUnsafe(2)
new DataView(target.buffer, target.byteOffset, target.byteLength).setUint16(0, value, false)
return target
}
uint16BEEncode.bytes = 2

export const uint16BEDecode = (data: Uint8Array | Uint8ArrayList): number => {
export const uint16BEDecode: LengthDecoderFunction = (data: Uint8Array | Uint8ArrayList): number => {
if (data.length < 2) throw RangeError('Could not decode int16BE')

if (data instanceof Uint8Array) {
Expand Down Expand Up @@ -49,8 +50,8 @@ export function decode0 (input: bytes): MessageBuffer {
}

return {
ne: input.slice(0, 32),
ciphertext: input.slice(32, input.length),
ne: input.subarray(0, 32),
ciphertext: input.subarray(32, input.length),
ns: new Uint8Array(0)
}
}
Expand All @@ -61,9 +62,9 @@ export function decode1 (input: bytes): MessageBuffer {
}

return {
ne: input.slice(0, 32),
ns: input.slice(32, 80),
ciphertext: input.slice(80, input.length)
ne: input.subarray(0, 32),
ns: input.subarray(32, 80),
ciphertext: input.subarray(80, input.length)
}
}

Expand All @@ -74,7 +75,7 @@ export function decode2 (input: bytes): MessageBuffer {

return {
ne: new Uint8Array(0),
ns: input.slice(0, 48),
ciphertext: input.slice(48, input.length)
ns: input.subarray(0, 48),
ciphertext: input.subarray(48, input.length)
}
}
2 changes: 1 addition & 1 deletion src/handshake-ik.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class IKHandshake implements IHandshake {
const err = e as Error
logger('Responder breaking up with IK handshake in stage 0.')

throw new FailedIKError(receivedMsg, `Error occurred while verifying initiator's signed payload: ${err.message}`)
throw new FailedIKError(receivedMsg.slice(), `Error occurred while verifying initiator's signed payload: ${err.message}`)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/handshake-xx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class XXHandshake implements IHandshake {
logLocalEphemeralKeys(this.session.hs.e)
} else {
logger('Stage 0 - Responder waiting to receive first message...')
const receivedMessageBuffer = decode0((await this.connection.readLP()).slice())
const receivedMessageBuffer = decode0((await this.connection.readLP()).subarray())
const { valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new InvalidCryptoExchangeError('xx handshake stage 0 validation fail')
Expand All @@ -83,7 +83,7 @@ export class XXHandshake implements IHandshake {
public async exchange (): Promise<void> {
if (this.isInitiator) {
logger('Stage 1 - Initiator waiting to receive first message from responder...')
const receivedMessageBuffer = decode1((await this.connection.readLP()).slice())
const receivedMessageBuffer = decode1((await this.connection.readLP()).subarray())
const { plaintext, valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new InvalidCryptoExchangeError('xx handshake stage 1 validation fail')
Expand Down Expand Up @@ -121,7 +121,7 @@ export class XXHandshake implements IHandshake {
logger('Stage 2 - Initiator sent message with signed payload.')
} else {
logger('Stage 2 - Responder waiting for third handshake message...')
const receivedMessageBuffer = decode2((await this.connection.readLP()).slice())
const receivedMessageBuffer = decode2((await this.connection.readLP()).subarray())
const { plaintext, valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
if (!valid) {
throw new InvalidCryptoExchangeError('xx handshake stage 2 validation fail')
Expand Down
2 changes: 1 addition & 1 deletion src/handshakes/abstract-handshake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export abstract class AbstractHandshake {
return derivedU8
}

return derivedU8.slice(0, 32)
return derivedU8.subarray(0, 32)
} catch (e) {
const err = e as Error
logger(err.message)
Expand Down
5 changes: 3 additions & 2 deletions src/proto/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { encodeMessage, decodeMessage, message, bytes } from 'protons-runtime'
import type { Codec } from 'protons-runtime'
import type { Uint8ArrayList } from 'uint8arraylist'

export namespace pb {
export interface NoiseHandshakePayload {
Expand All @@ -20,11 +21,11 @@ export namespace pb {
})
}

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

export const decode = (buf: Uint8Array): NoiseHandshakePayload => {
export const decode = (buf: Uint8Array | Uint8ArrayList): NoiseHandshakePayload => {
return decodeMessage(buf, NoiseHandshakePayload.codec())
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function createHandshakePayload (
identityKey: libp2pPublicKey,
identitySig: signedPayload,
data: earlyData ?? new Uint8Array(0)
})
}).subarray()
}

export async function signPayload (peerId: PeerId, payload: bytes): Promise<bytes> {
Expand Down
28 changes: 10 additions & 18 deletions test/noise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { duplexPair } from 'it-pair/duplex'
import { pbStream } from 'it-pb-stream'
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import { Uint8ArrayList } from 'uint8arraylist'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import sinon from 'sinon'
import { NOISE_MSG_MAX_LENGTH_BYTES } from '../src/constants.js'
import { stablelib } from '../src/crypto/stablelib.js'
Expand Down Expand Up @@ -95,23 +95,15 @@ describe('Noise', () => {
})()
])

try {
const wrappedOutbound = pbStream(outbound.conn)
wrappedOutbound.write(new Uint8ArrayList(Buffer.from('test')))

// Check that noise message is prefixed with 16-bit big-endian unsigned integer
const receivedEncryptedPayload = (await wrapped.read()).slice()
const view = new DataView(receivedEncryptedPayload.buffer, receivedEncryptedPayload.byteOffset, receivedEncryptedPayload.byteLength)
const dataLength = view.getInt16(0)
const data = receivedEncryptedPayload.slice(2, dataLength + 2)
const { plaintext: decrypted, valid } = handshake.decrypt(data, handshake.session)
// Decrypted data should match
assert(uint8ArrayEquals(decrypted, Buffer.from('test')))
assert(valid)
} catch (e) {
const err = e as Error
assert(false, err.message)
}
const wrappedOutbound = pbStream(outbound.conn)
wrappedOutbound.write(uint8ArrayFromString('test'))

// Check that noise message is prefixed with 16-bit big-endian unsigned integer
const data = await (await wrapped.readLP()).slice()
const { plaintext: decrypted, valid } = handshake.decrypt(data, handshake.session)
// Decrypted data should match
expect(uint8ArrayEquals(decrypted, uint8ArrayFromString('test'))).to.be.true()
expect(valid).to.be.true()
})

it('should test large payloads', async function () {
Expand Down
5 changes: 2 additions & 3 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { keys } from '@libp2p/crypto'
import type { PrivateKey } from '@libp2p/interface-keys'
import type { PeerId } from '@libp2p/interface-peer-id'
import { Buffer } from 'buffer'
import type { KeyPair } from '../src/@types/libp2p.js'

export async function generateEd25519Keys (): Promise<PrivateKey> {
Expand All @@ -14,7 +13,7 @@ export function getKeyPairFromPeerId (peerId: PeerId): KeyPair {
}

return {
privateKey: Buffer.from(peerId.privateKey.slice(0, 32)),
publicKey: Buffer.from(peerId.publicKey)
privateKey: peerId.privateKey.subarray(0, 32),
publicKey: peerId.publicKey
}
}