Skip to content

Commit

Permalink
clean up nostr cypto (#259)
Browse files Browse the repository at this point in the history
  • Loading branch information
BilligsterUser authored Nov 9, 2023
1 parent 97ca41f commit 48c4b2b
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 51 deletions.
67 changes: 21 additions & 46 deletions src/nostr/crypto.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,48 @@
import { l } from '@log'
import { secp256k1 } from '@noble/curves/secp256k1'
import { randomBytes } from '@noble/hashes/utils'
import { isErr } from '@util'
import { l } from '@src/logger'
import { isErr } from '@src/util'
import { Buffer } from 'buffer/'
import cjs from 'crypto-js'
import { AES, enc, lib, mode } from 'crypto-js'

const { AES, enc, lib, mode } = cjs

// eslint-disable-next-line @typescript-eslint/require-await, require-await
export async function encrypt(privkey: string, pubkey: string, text: string) {
function getEncKey(privkey: string, pubkey: string) {
const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)
const normalizedKey = getNormalizedX(key)
const iv = Uint8Array.from(randomBytes(16))
const cryptoKey = new Uint8Array(Buffer.from(normalizedKey))
const ciphertext = AES.encrypt(text,
byteArrayToWordArray(cryptoKey),
{ iv: byteArrayToWordArray(iv), mode: mode.CBC, }
)
const ctb64 = Buffer.from(wordArrayToByteArray(ciphertext.ciphertext)).toString('base64')
return key.slice(1, 33)
}
export function encrypt(privkey: string, pubkey: string, text: string) {
const iv = randomBytes(16)
const cryptoKey = getEncKey(privkey, pubkey)
const ciphertext = AES.encrypt(
text,
bytesToWordArr(cryptoKey),
{ iv: bytesToWordArr(iv), mode: mode.CBC }
).ciphertext
const ctb64 = enc.Base64.stringify(ciphertext)
const ivb64 = Buffer.from(iv).toString('base64')
return `${ctb64}?iv=${ivb64}`
}

// eslint-disable-next-line @typescript-eslint/require-await, require-await
export async function decrypt(privkey: string, pubkey: string, data: string) {
export function decrypt(privkey: string, pubkey: string, data: string) {
const [ctb64, ivb64] = data.split('?iv=')
const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)
const normalizedKey = getNormalizedX(key)
const iv = Buffer.from(ivb64, 'base64')
const cryptoKey = new Uint8Array(Buffer.from(normalizedKey))
const cryptoKey = getEncKey(privkey, pubkey)
const ciphertext = Buffer.from(ctb64, 'base64')
try {
const decrypted = AES.decrypt(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
/// @ts-expect-error
{ ciphertext: byteArrayToWordArray(Uint8Array.from(ciphertext)) },
byteArrayToWordArray(cryptoKey),
{ iv: byteArrayToWordArray(Uint8Array.from(iv)), mode: mode.CBC }
lib.CipherParams.create({ ciphertext: bytesToWordArr(ciphertext) }),
bytesToWordArr(cryptoKey),
{ iv: bytesToWordArr(iv), mode: mode.CBC }
)
return decrypted.toString(enc.Utf8)
} catch (e) {
l(isErr(e) ? e.message : e)
return ''
}
}

function getNormalizedX(key: Uint8Array): Uint8Array {
return key.slice(1, 33)
}

function byteArrayToWordArray(ba: Uint8Array) {
function bytesToWordArr(ba: Uint8Array) {
const wa: number[] = []
for (let i = 0; i < ba.length; i++) {
wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i)
}
return lib.WordArray.create(wa, ba.length)
}

function wordArrayToByteArray(wordArray: cjs.lib.WordArray) {
const len = wordArray.words.length
const u8_array = new Uint8Array(len << 2)
let offset = 0
let word
for (let i = 0; i < len; i++) {
word = wordArray.words[i]
u8_array[offset++] = word >> 24
u8_array[offset++] = (word >> 16) & 0xff
u8_array[offset++] = (word >> 8) & 0xff
u8_array[offset++] = word & 0xff
}
return u8_array
}
2 changes: 1 addition & 1 deletion src/screens/Payment/Processing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export default function ProcessingScreen({ navigation, route }: TProcessingPageP
return
}
const msg = `${userNostrNpub || nostr.senderName} (sender not verified) just sent you ${amount} Sat in Ecash using ${enutsPubkey}!\n\n ${token}`
const cipherTxt = await encrypt(sk, nostr.receiverNpub, msg)
const cipherTxt = encrypt(sk, nostr.receiverNpub, msg)
const event = {
kind: EventKind.DirectMessage,
tags: [['p', nostr.receiverNpub]],
Expand Down
8 changes: 4 additions & 4 deletions src/screens/Payment/Receive/nostrDM/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ export default function NostrDMScreen({ navigation, route }: TNostrReceivePagePr
const setDmsCB = useCallback((newDms: INostrDm[]) => setDms(newDms), [])

// decrypt dm, check for uniq cashu token and set dms state
const handleDm = async (sk: string, e: NostrEvent) => {
const handleDm = (sk: string, e: NostrEvent) => {
if (!sk.length) {
l('can not handle dms, empy key!')
return
}
const tokenMinLength = 25
// decrypt content
const decrypted = await decrypt(sk, e.pubkey, e.content)
const decrypted = decrypt(sk, e.pubkey, e.content)
// remove newlines (can be attached to the cashu token) and create an array of words
const words = decrypted.replace(/\n/g, ' ').split(' ')
// check each word of content
Expand Down Expand Up @@ -86,14 +86,14 @@ export default function NostrDMScreen({ navigation, route }: TNostrReceivePagePr
kinds: [EventKind.DirectMessage, EventKind.SetMetadata],
skipVerification: Config.skipVerification
})
sub?.on('event', async (e: NostrEvent) => {
sub?.on('event', (e: NostrEvent) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
if (+e.kind === EventKind.SetMetadata) {
setDmProfiles(prev => prev.some(x => x[0] === e.pubkey) ? prev : [...prev, [e.pubkey, parseProfileContent(e)]])
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
if (+e.kind === EventKind.DirectMessage) {
await handleDm(sk || '', e)
handleDm(sk || '', e)
}
})
sub?.on('eose', () => {
Expand Down

0 comments on commit 48c4b2b

Please sign in to comment.