feat: use nip04 for encryption and decryption of userData to store on blossom server
Some checks failed
Open PR on Staging / audit_and_check (pull_request) Failing after 17s

This commit is contained in:
daniyal 2024-08-22 13:08:30 +05:00
parent eb08760f87
commit 18270c5d8a
6 changed files with 83 additions and 16 deletions

View File

@ -18,3 +18,4 @@ export const SET_RELAY_MAP_UPDATED = 'SET_RELAY_MAP_UPDATED'
export const UPDATE_USER_APP_DATA = 'UPDATE_USER_APP_DATA' export const UPDATE_USER_APP_DATA = 'UPDATE_USER_APP_DATA'
export const UPDATE_PROCESSED_GIFT_WRAPS = 'UPDATE_PROCESSED_GIFT_WRAPS' export const UPDATE_PROCESSED_GIFT_WRAPS = 'UPDATE_PROCESSED_GIFT_WRAPS'
export const SET_D_TAG_FOR_APP_DATA = 'SET_D_TAG_FOR_APP_DATA'

View File

@ -1,6 +1,10 @@
import { UserAppData } from '../../types' import { UserAppData } from '../../types'
import * as ActionTypes from '../actionTypes' import * as ActionTypes from '../actionTypes'
import { UpdateProcessedGiftWraps, UpdateUserAppData } from './types' import {
SetDTagForAppData,
UpdateProcessedGiftWraps,
UpdateUserAppData
} from './types'
export const updateUserAppData = (payload: UserAppData): UpdateUserAppData => ({ export const updateUserAppData = (payload: UserAppData): UpdateUserAppData => ({
type: ActionTypes.UPDATE_USER_APP_DATA, type: ActionTypes.UPDATE_USER_APP_DATA,
@ -13,3 +17,8 @@ export const updateProcessedGiftWraps = (
type: ActionTypes.UPDATE_PROCESSED_GIFT_WRAPS, type: ActionTypes.UPDATE_PROCESSED_GIFT_WRAPS,
payload payload
}) })
export const setDTagForAppData = (payload: string): SetDTagForAppData => ({
type: ActionTypes.SET_D_TAG_FOR_APP_DATA,
payload
})

View File

@ -24,6 +24,12 @@ const reducer = (
processedGiftWraps: action.payload processedGiftWraps: action.payload
} }
case ActionTypes.SET_D_TAG_FOR_APP_DATA:
return {
...state,
dTag: action.payload
}
case ActionTypes.RESTORE_STATE: case ActionTypes.RESTORE_STATE:
return action.payload.userAppData || null return action.payload.userAppData || null

View File

@ -12,7 +12,13 @@ export interface UpdateProcessedGiftWraps {
payload: string[] payload: string[]
} }
export interface SetDTagForAppData {
type: typeof ActionTypes.SET_D_TAG_FOR_APP_DATA
payload: string
}
export type UserAppDataDispatchTypes = export type UserAppDataDispatchTypes =
| UpdateUserAppData | UpdateUserAppData
| UpdateProcessedGiftWraps | UpdateProcessedGiftWraps
| SetDTagForAppData
| RestoreState | RestoreState

View File

@ -43,6 +43,7 @@ export interface UserAppData {
sigits: { [key: string]: Meta } // key will be id of create signature sigits: { [key: string]: Meta } // key will be id of create signature
processedGiftWraps: string[] // an array of ids of processed gift wrapped events processedGiftWraps: string[] // an array of ids of processed gift wrapped events
keyPair?: Keys // this key pair is used for blossom requests authentication keyPair?: Keys // this key pair is used for blossom requests authentication
dTag?: string // this will be used for d tag in 30078 kind of events
blossomUrls: string[] // array for storing Urls for the files that stores all the sigits and processedGiftWraps on blossom blossomUrls: string[] // array for storing Urls for the files that stores all the sigits and processedGiftWraps on blossom
} }

View File

@ -11,6 +11,7 @@ import {
getEventHash, getEventHash,
getPublicKey, getPublicKey,
kinds, kinds,
nip04,
nip19, nip19,
nip44, nip44,
verifyEvent verifyEvent
@ -23,6 +24,7 @@ import {
relayController relayController
} from '../controllers' } from '../controllers'
import { import {
setDTagForAppData,
updateProcessedGiftWraps, updateProcessedGiftWraps,
updateUserAppData as updateUserAppDataAction updateUserAppData as updateUserAppDataAction
} from '../store/actions' } from '../store/actions'
@ -30,10 +32,54 @@ import { AuthState, Keys } from '../store/auth/types'
import { RelaysState } from '../store/relays/types' import { RelaysState } from '../store/relays/types'
import store from '../store/store' import store from '../store/store'
import { Meta, SignedEvent, UserAppData } from '../types' import { Meta, SignedEvent, UserAppData } from '../types'
import { getHash } from './hash' import { getDefaultRelayMap } from './relays'
import { parseJson, removeLeadingSlash } from './string' import { parseJson, removeLeadingSlash } from './string'
import { timeout } from './utils' import { timeout } from './utils'
import { getDefaultRelayMap } from './relays'
/**
* Generates a `d` tag for userAppData
*/
const getDTagForUserAppData = async (): Promise<string | null> => {
const isLoggedIn = store.getState().auth?.loggedIn
const pubkey = store.getState().auth?.usersPubkey
if (!isLoggedIn || !pubkey) {
throw new Error(
'For generating d tag user must be logged in and a valid pubkey should exists in app Store'
)
}
let dTag = store.getState().userAppData?.dTag
// if dTag is found in redux store then just return that
if (dTag) return dTag
// dTag not found is redux store. Generate it.
const unsignedEvent: UnsignedEvent = {
kind: kinds.ShortTextNote,
pubkey: pubkey,
created_at: 0,
tags: [],
content: `938_${pubkey}`
}
const nostrController = NostrController.getInstance()
const signedEvent = await nostrController
.signEvent(unsignedEvent)
.catch((err) => {
console.error('Failed to sign event for dTag', err)
toast.error(err.message || err)
return null
})
if (!signedEvent) return null
dTag = signedEvent.sig
// save dTag in redux store
store.dispatch(setDTagForAppData(dTag))
return dTag
}
/** /**
* @param hexKey hex private or public key * @param hexKey hex private or public key
@ -377,13 +423,13 @@ export const getUsersAppData = async (): Promise<UserAppData | null> => {
} }
// Generate an identifier for the user's nip78 // Generate an identifier for the user's nip78
const hash = await getHash('938' + usersPubkey) const dTag = await getDTagForUserAppData()
if (!hash) return null if (!dTag) return null
// Define a filter for fetching events // Define a filter for fetching events
const filter: Filter = { const filter: Filter = {
kinds: [kinds.Application], kinds: [kinds.Application],
'#d': [hash] '#d': [dTag]
} }
const encryptedContent = await relayController const encryptedContent = await relayController
@ -578,14 +624,14 @@ export const updateUsersAppData = async (meta: Meta) => {
if (!encryptedContent) return null if (!encryptedContent) return null
// generate the identifier for user's appData event // generate the identifier for user's appData event
const hash = await getHash('938' + usersPubkey) const dTag = await getDTagForUserAppData()
if (!hash) return null if (!dTag) return null
const updatedEvent: UnsignedEvent = { const updatedEvent: UnsignedEvent = {
kind: kinds.Application, kind: kinds.Application,
pubkey: usersPubkey!, pubkey: usersPubkey!,
created_at: unixNow(), created_at: unixNow(),
tags: [['d', hash]], tags: [['d', dTag]],
content: encryptedContent content: encryptedContent
} }
@ -693,9 +739,10 @@ const uploadUserAppDataToBlossom = async (
// Convert the private key from hex to bytes // Convert the private key from hex to bytes
const secretKey = hexToBytes(privateKey) const secretKey = hexToBytes(privateKey)
// Encrypt the JSON string using the secret key // Encrypt the JSON string using the secret key
const encrypted = nip44.v2.encrypt( const encrypted = await nip04.encrypt(
stringified, secretKey,
nip44ConversationKey(secretKey, getPublicKey(secretKey)) getPublicKey(secretKey),
stringified
) )
// Create a blob from the encrypted data // Create a blob from the encrypted data
@ -788,10 +835,7 @@ const getUserAppDataFromBlossom = async (url: string, privateKey: string) => {
const pubkey = getPublicKey(secret) const pubkey = getPublicKey(secret)
// Decrypt the encrypted data using the secret and public key // Decrypt the encrypted data using the secret and public key
const decrypted = nip44.v2.decrypt( const decrypted = await nip04.decrypt(secret, pubkey, encrypted)
encrypted,
nip44ConversationKey(secret, pubkey)
)
// Parse the decrypted JSON content // Parse the decrypted JSON content
const parsedContent = await parseJson<{ const parsedContent = await parseJson<{