Skip to content

Commit 9ebbfc5

Browse files
authored
feat!: refactor codebase (#407)
* chore: cleanup comments * fix: simplify uint16 encode/decode * fix: fix node crypto * chore: consolidate types * fix: avoid reallocations of empty key * fix: simplify XX initialization * chore: add more comments to types * fix: remove unused psk state variable * feat: refactor the codebase
1 parent 0ffa85f commit 9ebbfc5

25 files changed

Lines changed: 959 additions & 1279 deletions

src/@types/basic.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/@types/handshake-interface.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/@types/handshake.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/@types/libp2p.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/crypto.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
import { type Uint8ArrayList } from 'uint8arraylist'
2-
import type { bytes32 } from './@types/basic.js'
3-
import type { Hkdf } from './@types/handshake.js'
4-
import type { KeyPair } from './@types/libp2p.js'
1+
import type { ICrypto, KeyPair } from './types.js'
2+
import type { Uint8ArrayList } from 'uint8arraylist'
53

4+
/** Underlying crypto implementation, meant to be overridable */
65
export interface ICryptoInterface {
76
hashSHA256(data: Uint8Array | Uint8ArrayList): Uint8Array
87

9-
getHKDF(ck: bytes32, ikm: Uint8Array): Hkdf
8+
getHKDF(ck: Uint8Array, ikm: Uint8Array): [Uint8Array, Uint8Array, Uint8Array]
109

1110
generateX25519KeyPair(): KeyPair
1211
generateX25519KeyPairFromSeed(seed: Uint8Array): KeyPair
1312
generateX25519SharedKey(privateKey: Uint8Array | Uint8ArrayList, publicKey: Uint8Array | Uint8ArrayList): Uint8Array
1413

15-
chaCha20Poly1305Encrypt(plaintext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: bytes32): Uint8ArrayList | Uint8Array
16-
chaCha20Poly1305Decrypt(ciphertext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: bytes32, dst?: Uint8Array): Uint8ArrayList | Uint8Array | null
14+
chaCha20Poly1305Encrypt(plaintext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: Uint8Array): Uint8ArrayList | Uint8Array
15+
chaCha20Poly1305Decrypt(ciphertext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: Uint8Array, dst?: Uint8Array): Uint8ArrayList | Uint8Array
16+
}
17+
18+
export function wrapCrypto (crypto: ICryptoInterface): ICrypto {
19+
return {
20+
generateKeypair: crypto.generateX25519KeyPair,
21+
dh: (keypair, publicKey) => crypto.generateX25519SharedKey(keypair.privateKey, publicKey).subarray(0, 32),
22+
encrypt: crypto.chaCha20Poly1305Encrypt,
23+
decrypt: crypto.chaCha20Poly1305Decrypt,
24+
hash: crypto.hashSHA256,
25+
hkdf: crypto.getHKDF
26+
}
1727
}

src/crypto/index.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { digest } from '@chainsafe/as-sha256'
44
import { Uint8ArrayList } from 'uint8arraylist'
55
import { isElectronMain } from 'wherearewe'
66
import { pureJsCrypto } from './js.js'
7-
import type { KeyPair } from '../@types/libp2p.js'
87
import type { ICryptoInterface } from '../crypto.js'
8+
import type { KeyPair } from '../types.js'
99

1010
const ctx = newInstance()
1111
const asImpl = new ChaCha20Poly1305(ctx)
@@ -38,7 +38,7 @@ const nodeCrypto: Pick<ICryptoInterface, 'hashSHA256' | 'chaCha20Poly1305Encrypt
3838
const final = cipher.final()
3939
const tag = cipher.getAuthTag()
4040

41-
return Buffer.concat([updated, tag, final], updated.byteLength + tag.byteLength + final.byteLength)
41+
return Buffer.concat([updated, final, tag], updated.byteLength + final.byteLength + tag.byteLength)
4242
}
4343

4444
const output = new Uint8ArrayList()
@@ -112,7 +112,11 @@ const asCrypto: Pick<ICryptoInterface, 'hashSHA256' | 'chaCha20Poly1305Encrypt'
112112
return asImpl.seal(k, nonce, plaintext.subarray(), ad)
113113
},
114114
chaCha20Poly1305Decrypt (ciphertext, nonce, ad, k, dst) {
115-
return asImpl.open(k, nonce, ciphertext.subarray(), ad, dst)
115+
const plaintext = asImpl.open(k, nonce, ciphertext.subarray(), ad, dst)
116+
if (!plaintext) {
117+
throw new Error('Invalid chacha20poly1305 decryption')
118+
}
119+
return plaintext
116120
}
117121
}
118122

@@ -181,8 +185,7 @@ export const defaultCrypto: ICryptoInterface = {
181185
publicKey
182186
], X25519_PREFIX.byteLength + publicKey.byteLength)
183187
} else {
184-
publicKey.prepend(X25519_PREFIX)
185-
publicKey = publicKey.subarray()
188+
publicKey = new Uint8ArrayList(X25519_PREFIX, publicKey).subarray()
186189
}
187190

188191
if (privateKey instanceof Uint8Array) {
@@ -191,8 +194,7 @@ export const defaultCrypto: ICryptoInterface = {
191194
privateKey
192195
], PKCS8_PREFIX.byteLength + privateKey.byteLength)
193196
} else {
194-
privateKey.prepend(PKCS8_PREFIX)
195-
privateKey = privateKey.subarray()
197+
privateKey = new Uint8ArrayList(PKCS8_PREFIX, privateKey).subarray()
196198
}
197199

198200
return crypto.diffieHellman({

src/crypto/js.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@ import { chacha20poly1305 } from '@noble/ciphers/chacha'
22
import { x25519 } from '@noble/curves/ed25519'
33
import { extract, expand } from '@noble/hashes/hkdf'
44
import { sha256 } from '@noble/hashes/sha256'
5-
import type { bytes32 } from '../@types/basic.js'
6-
import type { Hkdf } from '../@types/handshake.js'
7-
import type { KeyPair } from '../@types/libp2p.js'
85
import type { ICryptoInterface } from '../crypto.js'
6+
import type { KeyPair } from '../types.js'
97
import type { Uint8ArrayList } from 'uint8arraylist'
108

119
export const pureJsCrypto: ICryptoInterface = {
1210
hashSHA256 (data: Uint8Array | Uint8ArrayList): Uint8Array {
1311
return sha256(data.subarray())
1412
},
1513

16-
getHKDF (ck: bytes32, ikm: Uint8Array): Hkdf {
14+
getHKDF (ck: Uint8Array, ikm: Uint8Array): [Uint8Array, Uint8Array, Uint8Array] {
1715
const prk = extract(sha256, ikm, ck)
1816
const okmU8Array = expand(sha256, prk, undefined, 96)
1917
const okm = okmU8Array
@@ -48,11 +46,11 @@ export const pureJsCrypto: ICryptoInterface = {
4846
return x25519.getSharedSecret(privateKey.subarray(), publicKey.subarray())
4947
},
5048

51-
chaCha20Poly1305Encrypt (plaintext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: bytes32): Uint8Array {
49+
chaCha20Poly1305Encrypt (plaintext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: Uint8Array): Uint8Array {
5250
return chacha20poly1305(k, nonce, ad).encrypt(plaintext.subarray())
5351
},
5452

55-
chaCha20Poly1305Decrypt (ciphertext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: bytes32, dst?: Uint8Array): Uint8Array | null {
53+
chaCha20Poly1305Decrypt (ciphertext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: Uint8Array, dst?: Uint8Array): Uint8Array {
5654
return chacha20poly1305(k, nonce, ad).decrypt(ciphertext.subarray(), dst)
5755
}
5856
}

src/encoder.ts

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { Uint8ArrayList } from 'uint8arraylist'
2-
import { alloc as uint8ArrayAlloc, allocUnsafe as uint8ArrayAllocUnsafe } from 'uint8arrays/alloc'
3-
import type { bytes } from './@types/basic.js'
4-
import type { MessageBuffer } from './@types/handshake.js'
1+
import { type Uint8ArrayList } from 'uint8arraylist'
2+
import { allocUnsafe as uint8ArrayAllocUnsafe } from 'uint8arrays/alloc'
53
import type { LengthDecoderFunction } from 'it-length-prefixed'
64

75
export const uint16BEEncode = (value: number): Uint8Array => {
86
const target = uint8ArrayAllocUnsafe(2)
9-
new DataView(target.buffer, target.byteOffset, target.byteLength).setUint16(0, value, false)
7+
target[0] = value >> 8
8+
target[1] = value
109
return target
1110
}
1211
uint16BEEncode.bytes = 2
@@ -15,59 +14,12 @@ export const uint16BEDecode: LengthDecoderFunction = (data: Uint8Array | Uint8Ar
1514
if (data.length < 2) throw RangeError('Could not decode int16BE')
1615

1716
if (data instanceof Uint8Array) {
18-
return new DataView(data.buffer, data.byteOffset, data.byteLength).getUint16(0, false)
17+
let value = 0
18+
value += data[0] << 8
19+
value += data[1]
20+
return value
1921
}
2022

2123
return data.getUint16(0)
2224
}
2325
uint16BEDecode.bytes = 2
24-
25-
// Note: IK and XX encoder usage is opposite (XX uses in stages encode0 where IK uses encode1)
26-
27-
export function encode0 (message: MessageBuffer): Uint8ArrayList {
28-
return new Uint8ArrayList(message.ne, message.ciphertext)
29-
}
30-
31-
export function encode1 (message: MessageBuffer): Uint8ArrayList {
32-
return new Uint8ArrayList(message.ne, message.ns, message.ciphertext)
33-
}
34-
35-
export function encode2 (message: MessageBuffer): Uint8ArrayList {
36-
return new Uint8ArrayList(message.ns, message.ciphertext)
37-
}
38-
39-
export function decode0 (input: bytes): MessageBuffer {
40-
if (input.length < 32) {
41-
throw new Error('Cannot decode stage 0 MessageBuffer: length less than 32 bytes.')
42-
}
43-
44-
return {
45-
ne: input.subarray(0, 32),
46-
ciphertext: input.subarray(32, input.length),
47-
ns: uint8ArrayAlloc(0)
48-
}
49-
}
50-
51-
export function decode1 (input: bytes): MessageBuffer {
52-
if (input.length < 80) {
53-
throw new Error('Cannot decode stage 1 MessageBuffer: length less than 80 bytes.')
54-
}
55-
56-
return {
57-
ne: input.subarray(0, 32),
58-
ns: input.subarray(32, 80),
59-
ciphertext: input.subarray(80, input.length)
60-
}
61-
}
62-
63-
export function decode2 (input: bytes): MessageBuffer {
64-
if (input.length < 48) {
65-
throw new Error('Cannot decode stage 2 MessageBuffer: length less than 48 bytes.')
66-
}
67-
68-
return {
69-
ne: uint8ArrayAlloc(0),
70-
ns: input.subarray(0, 48),
71-
ciphertext: input.subarray(48, input.length)
72-
}
73-
}

0 commit comments

Comments
 (0)