Skip to content

Commit 3fff6fd

Browse files
authored
Merge branch 'main' into feat/config-validation
2 parents f8e3193 + 4a474d5 commit 3fff6fd

File tree

51 files changed

+454
-347
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+454
-347
lines changed

packages/connection-encrypter-plaintext/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@
5050
"@libp2p/interface": "^0.1.2",
5151
"@libp2p/peer-id": "^3.0.6",
5252
"@multiformats/multiaddr": "^12.1.10",
53-
"it-handshake": "^4.1.3",
54-
"it-length-prefixed": "^9.0.3",
55-
"it-map": "^3.0.4",
53+
"it-protobuf-stream": "^1.1.1",
5654
"it-stream-types": "^2.0.1",
5755
"protons-runtime": "^5.0.0",
5856
"uint8arraylist": "^2.4.3"

packages/connection-encrypter-plaintext/src/index.ts

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -22,49 +22,53 @@
2222

2323
import { UnexpectedPeerError, InvalidCryptoExchangeError } from '@libp2p/interface/errors'
2424
import { peerIdFromBytes, peerIdFromKeys } from '@libp2p/peer-id'
25-
import { handshake } from 'it-handshake'
26-
import * as lp from 'it-length-prefixed'
27-
import map from 'it-map'
25+
import { pbStream } from 'it-protobuf-stream'
2826
import { Exchange, KeyType } from './pb/proto.js'
2927
import type { ComponentLogger, Logger } from '@libp2p/interface'
28+
import type { MultiaddrConnection } from '@libp2p/interface/connection'
3029
import type { ConnectionEncrypter, SecuredConnection } from '@libp2p/interface/connection-encrypter'
3130
import type { PeerId } from '@libp2p/interface/peer-id'
32-
import type { Duplex, Source } from 'it-stream-types'
31+
import type { Duplex } from 'it-stream-types'
3332
import type { Uint8ArrayList } from 'uint8arraylist'
3433

3534
const PROTOCOL = '/plaintext/2.0.0'
3635

37-
function lpEncodeExchange (exchange: Exchange): Uint8ArrayList {
38-
const pb = Exchange.encode(exchange)
39-
40-
return lp.encode.single(pb)
41-
}
42-
4336
export interface PlaintextComponents {
4437
logger: ComponentLogger
4538
}
4639

40+
export interface PlaintextInit {
41+
/**
42+
* The peer id exchange must complete within this many milliseconds
43+
* (default: 1000)
44+
*/
45+
timeout?: number
46+
}
47+
4748
class Plaintext implements ConnectionEncrypter {
4849
public protocol: string = PROTOCOL
4950
private readonly log: Logger
51+
private readonly timeout: number
5052

51-
constructor (components: PlaintextComponents) {
53+
constructor (components: PlaintextComponents, init: PlaintextInit = {}) {
5254
this.log = components.logger.forComponent('libp2p:plaintext')
55+
this.timeout = init.timeout ?? 1000
5356
}
5457

55-
async secureInbound (localId: PeerId, conn: Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>, Promise<void>>, remoteId?: PeerId): Promise<SecuredConnection> {
58+
async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
5659
return this._encrypt(localId, conn, remoteId)
5760
}
5861

59-
async secureOutbound (localId: PeerId, conn: Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>, Promise<void>>, remoteId?: PeerId): Promise<SecuredConnection> {
62+
async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
6063
return this._encrypt(localId, conn, remoteId)
6164
}
6265

6366
/**
6467
* Encrypt connection
6568
*/
66-
async _encrypt (localId: PeerId, conn: Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>, Promise<void>>, remoteId?: PeerId): Promise<SecuredConnection> {
67-
const shake = handshake(conn)
69+
async _encrypt <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
70+
const signal = AbortSignal.timeout(this.timeout)
71+
const pb = pbStream(conn).pb(Exchange)
6872

6973
let type = KeyType.RSA
7074

@@ -75,45 +79,40 @@ class Plaintext implements ConnectionEncrypter {
7579
}
7680

7781
// Encode the public key and write it to the remote peer
78-
shake.write(
79-
lpEncodeExchange({
80-
id: localId.toBytes(),
81-
pubkey: {
82-
Type: type,
83-
Data: localId.publicKey ?? new Uint8Array(0)
84-
}
85-
}).subarray()
86-
)
82+
await pb.write({
83+
id: localId.toBytes(),
84+
pubkey: {
85+
Type: type,
86+
Data: localId.publicKey ?? new Uint8Array(0)
87+
}
88+
}, {
89+
signal
90+
})
8791

8892
this.log('write pubkey exchange to peer %p', remoteId)
8993

9094
// Get the Exchange message
91-
const response = (await lp.decode.fromReader(shake.reader).next()).value
92-
93-
if (response == null) {
94-
throw new Error('Did not read response')
95-
}
96-
97-
const id = Exchange.decode(response)
98-
this.log('read pubkey exchange from peer %p', remoteId)
95+
const response = await pb.read({
96+
signal
97+
})
9998

10099
let peerId
101100
try {
102-
if (id.pubkey == null) {
101+
if (response.pubkey == null) {
103102
throw new Error('Public key missing')
104103
}
105104

106-
if (id.pubkey.Data.length === 0) {
105+
if (response.pubkey.Data.length === 0) {
107106
throw new Error('Public key data too short')
108107
}
109108

110-
if (id.id == null) {
109+
if (response.id == null) {
111110
throw new Error('Remote id missing')
112111
}
113112

114-
peerId = await peerIdFromKeys(id.pubkey.Data)
113+
peerId = await peerIdFromKeys(response.pubkey.Data)
115114

116-
if (!peerId.equals(peerIdFromBytes(id.id))) {
115+
if (!peerId.equals(peerIdFromBytes(response.id))) {
117116
throw new Error('Public key did not match id')
118117
}
119118
} catch (err: any) {
@@ -127,18 +126,13 @@ class Plaintext implements ConnectionEncrypter {
127126

128127
this.log('plaintext key exchange completed successfully with peer %p', peerId)
129128

130-
shake.rest()
131-
132129
return {
133-
conn: {
134-
sink: shake.stream.sink,
135-
source: map(shake.stream.source, (buf) => buf.subarray())
136-
},
130+
conn: pb.unwrap().unwrap(),
137131
remotePeer: peerId
138132
}
139133
}
140134
}
141135

142-
export function plaintext (): (components: PlaintextComponents) => ConnectionEncrypter {
143-
return (components) => new Plaintext(components)
136+
export function plaintext (init?: PlaintextInit): (components: PlaintextComponents) => ConnectionEncrypter {
137+
return (components) => new Plaintext(components, init)
144138
}

packages/crypto/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
"./dist/src/hmac/index.js": "./dist/src/hmac/index-browser.js",
108108
"./dist/src/keys/ecdh.js": "./dist/src/keys/ecdh-browser.js",
109109
"./dist/src/keys/ed25519.js": "./dist/src/keys/ed25519-browser.js",
110-
"./dist/src/keys/rsa.js": "./dist/src/keys/rsa-browser.js"
110+
"./dist/src/keys/rsa.js": "./dist/src/keys/rsa-browser.js",
111+
"./dist/src/keys/secp256k1.js": "./dist/src/keys/secp256k1-browser.js"
111112
}
112113
}

packages/crypto/src/keys/ed25519-browser.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ed25519 as ed } from '@noble/curves/ed25519'
22
import type { Uint8ArrayKeyPair } from './interface'
3+
import type { Uint8ArrayList } from 'uint8arraylist'
34

45
const PUBLIC_KEY_BYTE_LENGTH = 32
56
const PRIVATE_KEY_BYTE_LENGTH = 64 // private key is actually 32 bytes but for historical reasons we concat private and public keys
@@ -44,14 +45,14 @@ export async function generateKeyFromSeed (seed: Uint8Array): Promise<Uint8Array
4445
}
4546
}
4647

47-
export async function hashAndSign (privateKey: Uint8Array, msg: Uint8Array): Promise<Uint8Array> {
48+
export async function hashAndSign (privateKey: Uint8Array, msg: Uint8Array | Uint8ArrayList): Promise<Uint8Array> {
4849
const privateKeyRaw = privateKey.subarray(0, KEYS_BYTE_LENGTH)
4950

50-
return ed.sign(msg, privateKeyRaw)
51+
return ed.sign(msg instanceof Uint8Array ? msg : msg.subarray(), privateKeyRaw)
5152
}
5253

53-
export async function hashAndVerify (publicKey: Uint8Array, sig: Uint8Array, msg: Uint8Array): Promise<boolean> {
54-
return ed.verify(sig, msg, publicKey)
54+
export async function hashAndVerify (publicKey: Uint8Array, sig: Uint8Array, msg: Uint8Array | Uint8ArrayList): Promise<boolean> {
55+
return ed.verify(sig, msg instanceof Uint8Array ? msg : msg.subarray(), publicKey)
5556
}
5657

5758
function concatKeys (privateKeyRaw: Uint8Array, publicKey: Uint8Array): Uint8Array {

packages/crypto/src/keys/ed25519-class.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as crypto from './ed25519.js'
77
import { exporter } from './exporter.js'
88
import * as pbm from './keys.js'
99
import type { Multibase } from 'multiformats'
10+
import type { Uint8ArrayList } from 'uint8arraylist'
1011

1112
export class Ed25519PublicKey {
1213
private readonly _key: Uint8Array
@@ -15,7 +16,7 @@ export class Ed25519PublicKey {
1516
this._key = ensureKey(key, crypto.publicKeyLength)
1617
}
1718

18-
async verify (data: Uint8Array, sig: Uint8Array): Promise<boolean> {
19+
async verify (data: Uint8Array | Uint8ArrayList, sig: Uint8Array): Promise<boolean> {
1920
return crypto.hashAndVerify(this._key, sig, data)
2021
}
2122

@@ -52,7 +53,7 @@ export class Ed25519PrivateKey {
5253
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
5354
}
5455

55-
async sign (message: Uint8Array): Promise<Uint8Array> {
56+
async sign (message: Uint8Array | Uint8ArrayList): Promise<Uint8Array> {
5657
return crypto.hashAndSign(this._key, message)
5758
}
5859

packages/crypto/src/keys/ed25519.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import crypto from 'crypto'
22
import { promisify } from 'util'
3+
import { concat as uint8arrayConcat } from 'uint8arrays/concat'
34
import { fromString as uint8arrayFromString } from 'uint8arrays/from-string'
45
import { toString as uint8arrayToString } from 'uint8arrays/to-string'
56
import type { Uint8ArrayKeyPair } from './interface.js'
7+
import type { Uint8ArrayList } from 'uint8arraylist'
68

79
const keypair = promisify(crypto.generateKeyPair)
810

@@ -47,7 +49,7 @@ export async function generateKey (): Promise<Uint8ArrayKeyPair> {
4749
const publicKeyRaw = uint8arrayFromString(key.privateKey.x, 'base64url')
4850

4951
return {
50-
privateKey: concatKeys(privateKeyRaw, publicKeyRaw),
52+
privateKey: uint8arrayConcat([privateKeyRaw, publicKeyRaw], privateKeyRaw.byteLength + publicKeyRaw.byteLength),
5153
publicKey: publicKeyRaw
5254
}
5355
}
@@ -66,12 +68,12 @@ export async function generateKeyFromSeed (seed: Uint8Array): Promise<Uint8Array
6668
const publicKeyRaw = derivePublicKey(seed)
6769

6870
return {
69-
privateKey: concatKeys(seed, publicKeyRaw),
71+
privateKey: uint8arrayConcat([seed, publicKeyRaw], seed.byteLength + publicKeyRaw.byteLength),
7072
publicKey: publicKeyRaw
7173
}
7274
}
7375

74-
export async function hashAndSign (key: Uint8Array, msg: Uint8Array): Promise<Buffer> {
76+
export async function hashAndSign (key: Uint8Array, msg: Uint8Array | Uint8ArrayList): Promise<Buffer> {
7577
if (!(key instanceof Uint8Array)) {
7678
throw new TypeError('"key" must be a node.js Buffer, or Uint8Array.')
7779
}
@@ -99,10 +101,10 @@ export async function hashAndSign (key: Uint8Array, msg: Uint8Array): Promise<Bu
99101
}
100102
})
101103

102-
return crypto.sign(null, msg, obj)
104+
return crypto.sign(null, msg instanceof Uint8Array ? msg : msg.subarray(), obj)
103105
}
104106

105-
export async function hashAndVerify (key: Uint8Array, sig: Uint8Array, msg: Uint8Array): Promise<boolean> {
107+
export async function hashAndVerify (key: Uint8Array, sig: Uint8Array, msg: Uint8Array | Uint8ArrayList): Promise<boolean> {
106108
if (key.byteLength !== PUBLIC_KEY_BYTE_LENGTH) {
107109
throw new TypeError('"key" must be 32 bytes in length.')
108110
} else if (!(key instanceof Uint8Array)) {
@@ -124,14 +126,5 @@ export async function hashAndVerify (key: Uint8Array, sig: Uint8Array, msg: Uint
124126
}
125127
})
126128

127-
return crypto.verify(null, msg, obj, sig)
128-
}
129-
130-
function concatKeys (privateKeyRaw: Uint8Array, publicKey: Uint8Array): Uint8Array {
131-
const privateKey = new Uint8Array(PRIVATE_KEY_BYTE_LENGTH)
132-
for (let i = 0; i < KEYS_BYTE_LENGTH; i++) {
133-
privateKey[i] = privateKeyRaw[i]
134-
privateKey[KEYS_BYTE_LENGTH + i] = publicKey[i]
135-
}
136-
return privateKey
129+
return crypto.verify(null, msg instanceof Uint8Array ? msg : msg.subarray(), obj, sig)
137130
}

packages/crypto/src/keys/rsa-browser.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import webcrypto from '../webcrypto.js'
66
import { jwk2pub, jwk2priv } from './jwk2pem.js'
77
import * as utils from './rsa-utils.js'
88
import type { JWKKeyPair } from './interface.js'
9+
import type { Uint8ArrayList } from 'uint8arraylist'
910

1011
export { utils }
1112

@@ -60,7 +61,7 @@ export async function unmarshalPrivateKey (key: JsonWebKey): Promise<JWKKeyPair>
6061

6162
export { randomBytes as getRandomValues }
6263

63-
export async function hashAndSign (key: JsonWebKey, msg: Uint8Array): Promise<Uint8Array> {
64+
export async function hashAndSign (key: JsonWebKey, msg: Uint8Array | Uint8ArrayList): Promise<Uint8Array> {
6465
const privateKey = await webcrypto.get().subtle.importKey(
6566
'jwk',
6667
key,
@@ -75,13 +76,13 @@ export async function hashAndSign (key: JsonWebKey, msg: Uint8Array): Promise<Ui
7576
const sig = await webcrypto.get().subtle.sign(
7677
{ name: 'RSASSA-PKCS1-v1_5' },
7778
privateKey,
78-
Uint8Array.from(msg)
79+
msg instanceof Uint8Array ? msg : msg.subarray()
7980
)
8081

8182
return new Uint8Array(sig, 0, sig.byteLength)
8283
}
8384

84-
export async function hashAndVerify (key: JsonWebKey, sig: Uint8Array, msg: Uint8Array): Promise<boolean> {
85+
export async function hashAndVerify (key: JsonWebKey, sig: Uint8Array, msg: Uint8Array | Uint8ArrayList): Promise<boolean> {
8586
const publicKey = await webcrypto.get().subtle.importKey(
8687
'jwk',
8788
key,
@@ -97,7 +98,7 @@ export async function hashAndVerify (key: JsonWebKey, sig: Uint8Array, msg: Uint
9798
{ name: 'RSASSA-PKCS1-v1_5' },
9899
publicKey,
99100
sig,
100-
msg
101+
msg instanceof Uint8Array ? msg : msg.subarray()
101102
)
102103
}
103104

@@ -141,18 +142,18 @@ Explanation:
141142
142143
*/
143144

144-
function convertKey (key: JsonWebKey, pub: boolean, msg: Uint8Array, handle: (msg: string, key: { encrypt(msg: string): string, decrypt(msg: string): string }) => string): Uint8Array {
145+
function convertKey (key: JsonWebKey, pub: boolean, msg: Uint8Array | Uint8ArrayList, handle: (msg: string, key: { encrypt(msg: string): string, decrypt(msg: string): string }) => string): Uint8Array {
145146
const fkey = pub ? jwk2pub(key) : jwk2priv(key)
146-
const fmsg = uint8ArrayToString(Uint8Array.from(msg), 'ascii')
147+
const fmsg = uint8ArrayToString(msg instanceof Uint8Array ? msg : msg.subarray(), 'ascii')
147148
const fomsg = handle(fmsg, fkey)
148149
return uint8ArrayFromString(fomsg, 'ascii')
149150
}
150151

151-
export function encrypt (key: JsonWebKey, msg: Uint8Array): Uint8Array {
152+
export function encrypt (key: JsonWebKey, msg: Uint8Array | Uint8ArrayList): Uint8Array {
152153
return convertKey(key, true, msg, (msg, key) => key.encrypt(msg))
153154
}
154155

155-
export function decrypt (key: JsonWebKey, msg: Uint8Array): Uint8Array {
156+
export function decrypt (key: JsonWebKey, msg: Uint8Array | Uint8ArrayList): Uint8Array {
156157
return convertKey(key, false, msg, (msg, key) => key.decrypt(msg))
157158
}
158159

packages/crypto/src/keys/rsa-class.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { exporter } from './exporter.js'
99
import * as pbm from './keys.js'
1010
import * as crypto from './rsa.js'
1111
import type { Multibase } from 'multiformats'
12+
import type { Uint8ArrayList } from 'uint8arraylist'
1213

1314
export const MAX_KEY_SIZE = 8192
1415

@@ -19,7 +20,7 @@ export class RsaPublicKey {
1920
this._key = key
2021
}
2122

22-
async verify (data: Uint8Array, sig: Uint8Array): Promise<boolean> {
23+
async verify (data: Uint8Array | Uint8ArrayList, sig: Uint8Array): Promise<boolean> {
2324
return crypto.hashAndVerify(this._key, sig, data)
2425
}
2526

@@ -34,7 +35,7 @@ export class RsaPublicKey {
3435
}).subarray()
3536
}
3637

37-
encrypt (bytes: Uint8Array): Uint8Array {
38+
encrypt (bytes: Uint8Array | Uint8ArrayList): Uint8Array {
3839
return crypto.encrypt(this._key, bytes)
3940
}
4041

@@ -62,7 +63,7 @@ export class RsaPrivateKey {
6263
return crypto.getRandomValues(16)
6364
}
6465

65-
async sign (message: Uint8Array): Promise<Uint8Array> {
66+
async sign (message: Uint8Array | Uint8ArrayList): Promise<Uint8Array> {
6667
return crypto.hashAndSign(this._key, message)
6768
}
6869

@@ -74,7 +75,7 @@ export class RsaPrivateKey {
7475
return new RsaPublicKey(this._publicKey)
7576
}
7677

77-
decrypt (bytes: Uint8Array): Uint8Array {
78+
decrypt (bytes: Uint8Array | Uint8ArrayList): Uint8Array {
7879
return crypto.decrypt(this._key, bytes)
7980
}
8081

0 commit comments

Comments
 (0)