Skip to content
Closed
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@cacheable/node-cache": "^1.4.0",
"@hapi/boom": "^9.1.3",
"async-mutex": "^0.5.0",
"libsignal": "git+https://github.com/whiskeysockets/libsignal-node",
"libsignal": "git+https://github.com/jlucaso1/libsignal-node#feat-custom-logger",
"lru-cache": "^11.1.0",
"music-metadata": "^11.7.0",
"p-queue": "^9.0.0",
Expand Down
16 changes: 8 additions & 8 deletions src/Signal/libsignal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* @ts-ignore */
import * as libsignal from 'libsignal'
import { LRUCache } from 'lru-cache'
import type { LIDMapping, SignalAuthState, SignalKeyStoreWithTransaction } from '../Types'
Expand Down Expand Up @@ -26,7 +25,7 @@ export function makeLibSignalRepository(
pnToLIDFunc?: (jids: string[]) => Promise<LIDMapping[] | undefined>
): SignalRepositoryWithLIDStore {
const lidMapping = new LIDMappingStore(auth.keys as SignalKeyStoreWithTransaction, logger, pnToLIDFunc)
const storage = signalStorage(auth, lidMapping)
const storage = signalStorage(auth, lidMapping, logger)

const parsedKeys = auth.keys as SignalKeyStoreWithTransaction
const migratedSessionCache = new LRUCache<string, true>({
Expand Down Expand Up @@ -77,7 +76,7 @@ export function makeLibSignalRepository(
},
async decryptMessage({ jid, type, ciphertext }) {
const addr = jidToSignalProtocolAddress(jid)
const session = new libsignal.SessionCipher(storage, addr)
const session = new libsignal.SessionCipher(storage, addr, logger)

async function doDecrypt() {
let result: Buffer
Expand All @@ -102,7 +101,7 @@ export function makeLibSignalRepository(

async encryptMessage({ jid, data }) {
const addr = jidToSignalProtocolAddress(jid)
const cipher = new libsignal.SessionCipher(storage, addr)
const cipher = new libsignal.SessionCipher(storage, addr, logger)

// Use transaction to ensure atomicity
return parsedKeys.transaction(async () => {
Expand Down Expand Up @@ -137,7 +136,7 @@ export function makeLibSignalRepository(

async injectE2ESession({ jid, session }) {
logger.trace({ jid }, 'injecting E2EE session')
const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid))
const cipher = new libsignal.SessionBuilder(storage, jidToSignalProtocolAddress(jid), logger)
return parsedKeys.transaction(async () => {
await cipher.initOutgoing(session)
}, jid)
Expand Down Expand Up @@ -296,7 +295,7 @@ export function makeLibSignalRepository(
const pnSession = pnSessions[pnAddrStr]
if (pnSession) {
// Session exists (guaranteed from device discovery)
const fromSession = libsignal.SessionRecord.deserialize(pnSession)
const fromSession = libsignal.SessionRecord.deserialize(pnSession, logger)
if (fromSession.haveOpenSession()) {
// Queue for bulk update: copy to LID, delete from PN
sessionUpdates[lidAddrStr] = fromSession.serialize()
Expand Down Expand Up @@ -358,7 +357,8 @@ const jidToSignalSenderKeyName = (group: string, user: string): SenderKeyName =>

function signalStorage(
{ creds, keys }: SignalAuthState,
lidMapping: LIDMappingStore
lidMapping: LIDMappingStore,
logger?: ILogger
): SenderKeyStore & libsignal.SignalStorage {
// Shared function to resolve PN signal address to LID if mapping exists
const resolveLIDSignalAddress = async (id: string): Promise<string> => {
Expand Down Expand Up @@ -388,7 +388,7 @@ function signalStorage(
const { [wireJid]: sess } = await keys.get('session', [wireJid])

if (sess) {
return libsignal.SessionRecord.deserialize(sess)
return libsignal.SessionRecord.deserialize(sess, logger)
}
} catch (e) {
return null
Expand Down
9 changes: 7 additions & 2 deletions src/Socket/messages-recv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,14 @@
return
}

let data: any

Check warning on line 181 in src/Socket/messages-recv.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
try {
data = JSON.parse(mexNode.content.toString())
} catch (error) {
logger.error({ err: error, node }, 'Failed to parse mex newsletter notification')
return
}

Check warning on line 188 in src/Socket/messages-recv.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
const operation = data?.operation
const updates = data?.updates

Expand Down Expand Up @@ -274,14 +274,14 @@
case 'update':
const settingsNode = getBinaryNodeChild(child, 'settings')
if (settingsNode) {
const update: Record<string, any> = {}

Check warning on line 277 in src/Socket/messages-recv.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
const nameNode = getBinaryNodeChild(settingsNode, 'name')
if (nameNode?.content) update.name = nameNode.content.toString()

const descriptionNode = getBinaryNodeChild(settingsNode, 'description')
if (descriptionNode?.content) update.description = descriptionNode.content.toString()

ev.emit('newsletter-settings.update', {

Check warning on line 284 in src/Socket/messages-recv.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
id: from,
update
})
Expand Down Expand Up @@ -815,7 +815,8 @@
const codePairingPublicKey = await decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped)
const companionSharedKey = Curve.sharedKey(
authState.creds.pairingEphemeralKeyPair.private,
codePairingPublicKey
codePairingPublicKey,
logger
)
const random = randomBytes(32)
const linkCodeSalt = randomBytes(32)
Expand All @@ -831,7 +832,11 @@
const encryptIv = randomBytes(12)
const encrypted = aesEncryptGCM(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0))
const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted])
const identitySharedKey = Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey)
const identitySharedKey = Curve.sharedKey(
authState.creds.signedIdentityKey.private,
primaryIdentityPublicKey,
logger
)
const identityPayload = Buffer.concat([companionSharedKey, identitySharedKey, random])
authState.creds.advSecretKey = (await hkdf(identityPayload, 32, { info: 'adv_secret' })).toString('base64')
await query({
Expand Down
2 changes: 1 addition & 1 deletion src/Socket/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@

const msgId = node.attrs.id

const result = await promiseTimeout<any>(timeoutMs, async (resolve, reject) => {

Check warning on line 192 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
const result = waitForMessage(msgId, timeoutMs).catch(reject)
sendNode(node)
.then(async () => resolve(await result))

Check warning on line 195 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
.catch(reject)
})

Expand Down Expand Up @@ -790,7 +790,7 @@
ws.on('open', async () => {
try {
await validateConnection()
} catch (err: any) {

Check warning on line 793 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
logger.error({ err }, 'error in validating connection')
end(err)
}
Expand Down Expand Up @@ -826,7 +826,7 @@
}

const refNode = refNodes.shift()
if (!refNode) {

Check warning on line 829 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
end(new Boom('QR refs attempts ended', { statusCode: DisconnectReason.timedOut }))
return
}
Expand All @@ -847,7 +847,7 @@
ws.on('CB:iq,,pair-success', async (stanza: BinaryNode) => {
logger.debug('pair success recv')
try {
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds)
const { reply, creds: updatedCreds } = configureSuccessfulPairing(stanza, creds, logger)

logger.info(
{ me: updatedCreds.me, platform: updatedCreds.platform },
Expand All @@ -858,7 +858,7 @@
ev.emit('connection.update', { isNewLogin: true, qr: undefined })

await sendNode(reply)
} catch (error: any) {

Check warning on line 861 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
logger.info({ trace: error.stack }, 'error in pairing')
end(error)
}
Expand Down Expand Up @@ -894,7 +894,7 @@
'device-list': {
[user]: [device?.toString() || '0']
}
})

Check warning on line 897 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type

// migrate our own session
await signalRepository.migrateSession(myPN, myLID)
Expand Down
9 changes: 5 additions & 4 deletions src/Utils/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes }
import * as curve from 'libsignal/src/curve'
import { KEY_BUNDLE_TYPE } from '../Defaults'
import type { KeyPair } from '../Types'
import type { ILogger } from './logger'

// insure browser & node compatibility
const { subtle } = globalThis.crypto
Expand All @@ -19,14 +20,14 @@ export const Curve = {
public: Buffer.from(pubKey.slice(1))
}
},
sharedKey: (privateKey: Uint8Array, publicKey: Uint8Array) => {
const shared = curve.calculateAgreement(generateSignalPubKey(publicKey), privateKey)
sharedKey: (privateKey: Uint8Array, publicKey: Uint8Array, logger?: ILogger) => {
const shared = curve.calculateAgreement(generateSignalPubKey(publicKey), privateKey, logger)
return Buffer.from(shared)
},
sign: (privateKey: Uint8Array, buf: Uint8Array) => curve.calculateSignature(privateKey, buf),
verify: (pubKey: Uint8Array, message: Uint8Array, signature: Uint8Array) => {
verify: (pubKey: Uint8Array, message: Uint8Array, signature: Uint8Array, logger?: ILogger) => {
try {
curve.verifySignature(generateSignalPubKey(pubKey), message, signature)
curve.verifySignature(generateSignalPubKey(pubKey), message, signature, logger)
return true
} catch (error) {
return false
Expand Down
6 changes: 3 additions & 3 deletions src/Utils/noise-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ export const makeNoiseHandler = ({
finishInit,
processHandshake: async ({ serverHello }: proto.HandshakeMessage, noiseKey: KeyPair) => {
authenticate(serverHello!.ephemeral!)
await mixIntoKey(Curve.sharedKey(privateKey, serverHello!.ephemeral!))
await mixIntoKey(Curve.sharedKey(privateKey, serverHello!.ephemeral!, logger))

const decStaticContent = decrypt(serverHello!.static!)
await mixIntoKey(Curve.sharedKey(privateKey, decStaticContent))
await mixIntoKey(Curve.sharedKey(privateKey, decStaticContent, logger))

const certDecoded = decrypt(serverHello!.payload!)

Expand All @@ -121,7 +121,7 @@ export const makeNoiseHandler = ({
}

const keyEnc = encrypt(noiseKey.public)
await mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello!.ephemeral!))
await mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello!.ephemeral!, logger))

return keyEnc
},
Expand Down
6 changes: 4 additions & 2 deletions src/Utils/validate-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { AuthenticationCreds, SignalCreds, SocketConfig } from '../Types'
import { type BinaryNode, getBinaryNodeChild, jidDecode, S_WHATSAPP_NET } from '../WABinary'
import { Curve, hmacSign } from './crypto'
import { encodeBigEndian } from './generics'
import type { ILogger } from './logger'
import { createSignalIdentity } from './signal'

const getUserAgent = (config: SocketConfig): proto.ClientPayload.IUserAgent => {
Expand Down Expand Up @@ -145,7 +146,8 @@ export const configureSuccessfulPairing = (
advSecretKey,
signedIdentityKey,
signalIdentities
}: Pick<AuthenticationCreds, 'advSecretKey' | 'signedIdentityKey' | 'signalIdentities'>
}: Pick<AuthenticationCreds, 'advSecretKey' | 'signedIdentityKey' | 'signalIdentities'>,
logger?: ILogger
) => {
const msgId = stanza.attrs.id

Expand Down Expand Up @@ -186,7 +188,7 @@ export const configureSuccessfulPairing = (
? WA_ADV_HOSTED_ACCOUNT_SIG_PREFIX
: WA_ADV_ACCOUNT_SIG_PREFIX
const accountMsg = Buffer.concat([accountSignaturePrefix, deviceDetails!, signedIdentityKey.public])
if (!Curve.verify(accountSignatureKey!, accountMsg, accountSignature!)) {
if (!Curve.verify(accountSignatureKey!, accountMsg, accountSignature!, logger)) {
throw new Boom('Failed to verify account signature')
}

Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3007,7 +3007,7 @@ __metadata:
jimp: "npm:^1.6.0"
jiti: "npm:^2.4.2"
json: "npm:^11.0.0"
libsignal: "git+https://github.com/whiskeysockets/libsignal-node"
libsignal: "git+https://github.com/jlucaso1/libsignal-node#feat-custom-logger"
link-preview-js: "npm:^3.0.0"
lru-cache: "npm:^11.1.0"
music-metadata: "npm:^11.7.0"
Expand Down Expand Up @@ -6976,13 +6976,13 @@ __metadata:
languageName: node
linkType: hard

"libsignal@git+https://github.com/whiskeysockets/libsignal-node":
"libsignal@git+https://github.com/jlucaso1/libsignal-node#feat-custom-logger":
version: 2.0.1
resolution: "libsignal@https://github.com/whiskeysockets/libsignal-node.git#commit=e81ecfc32eb74951d789ab37f7e341ab66d5fff1"
resolution: "libsignal@https://github.com/jlucaso1/libsignal-node.git#commit=98e361089320a9dbac04e5304ede77b7dfa1628a"
dependencies:
curve25519-js: "npm:^0.0.4"
protobufjs: "npm:6.8.8"
checksum: 10c0/d1ae7d8a5fadd6bb1c486d1b2ebc388967fee57c13f52b473127c1cbd9cd647b44545ff07c2b9cc49b3dea4e25ccfcfece31c526fdbdbf065837c85d189e97a0
checksum: 10c0/61c3589038f85ccc85fdf925bdf9a876f93642e5e591c3b6a123ba71c183bd9bb5220f229dbe5c8267cdc95ad57b6103e5a6e1b5f60593b75882be29ff838046
languageName: node
linkType: hard

Expand Down
Loading