Skip to content

Commit

Permalink
feat: pin_derived_eph_pub+priv
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
Berend Sliedrecht committed Jul 29, 2024
1 parent 4ffd02f commit 7f1b4ad
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 72 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,6 @@ android

# build output
dist/
types/
packages/ui/types

storybook-static
storybook-static
1 change: 1 addition & 0 deletions apps/funke/app/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const FUNKE_WALLET_INSTANCE_LONG_TERM_AES_KEY_ID = 'FUNKE_WALLET_INSTANCE_LONG_TERM_AES_KEY_ID'
4 changes: 4 additions & 0 deletions apps/funke/app/crypto/aes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { aes128Gcm } from '@package/agent'
import { FUNKE_WALLET_INSTANCE_LONG_TERM_AES_KEY_ID } from '../constants'

export const funkeAes128Gcm = aes128Gcm(FUNKE_WALLET_INSTANCE_LONG_TERM_AES_KEY_ID)
34 changes: 34 additions & 0 deletions apps/funke/app/crypto/bPrime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { AgentContext } from '@credo-ts/core'
import { Key, KeyAlgs, KeyMethod } from '@hyperledger/aries-askar-react-native'
import { kdf } from '@package/secure-store/kdf'
import { funkeAes128Gcm } from './aes'

/**
*
* Derive a key pair based on a numeric pin according to the steps in B'
*
* returns pin_derived_eph_pub + pin_derived_eph_priv
*
* @todo Might be good later to add methods like `signWithPidPin`
*
*/
export const deriveKeypairFromPin = async (agentContext: AgentContext, pin: Array<number>) => {
if (!(await funkeAes128Gcm.aes128GcmHasKey({ agentContext }))) {
throw new Error('No AES key found in storage. Flow is called in an incorrect way!')
}

const normalizedPin = new Uint8Array(pin)

const pinSecret = await funkeAes128Gcm.aes128GcmEncrypt({
agentContext,
data: normalizedPin,
})

const pinSeed = await kdf.derive(normalizedPin, pinSecret)

return Key.fromSeed({
seed: new Uint8Array(Buffer.from(pinSeed, 'hex')),
method: KeyMethod.None,
algorithm: KeyAlgs.EcSecp256r1,
})
}
2 changes: 2 additions & 0 deletions apps/funke/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@package/agent": "workspace:*",
"@package/app": "workspace:*",
"@package/ui": "workspace:*",
"@package/secure-store": "workspace:*",
"@react-native-community/blur": "^4.3.2",
"@react-native-community/netinfo": "11.3.1",
"@react-native-masked-view/masked-view": "0.3.1",
Expand Down Expand Up @@ -54,6 +55,7 @@
},
"devDependencies": {
"@babel/core": "^7.24.4",
"@credo-ts/core": "0.5.1-alpha.51",
"@tamagui/babel-plugin": "^1.104.2",
"typescript": "*"
}
Expand Down
63 changes: 63 additions & 0 deletions packages/agent/src/crypto/aes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { assertAskarWallet } from '@credo-ts/askar/build/utils/assertAskarWallet'
import type { AgentContext } from '@credo-ts/core'
import { Key, KeyAlgs } from '@hyperledger/aries-askar-react-native'

const aes128GcmGenerateAndStoreKey =
(id: string) =>
async ({ agentContext }: { agentContext: AgentContext }) => {
const wallet = agentContext.wallet
const key = Key.generate(KeyAlgs.AesA128Gcm)
assertAskarWallet(wallet)
await wallet.withSession((session) => session.insertKey({ name: id, key }))
}

const aes128GcmHasKey =
(id: string) =>
async ({ agentContext }: { agentContext: AgentContext }) => {
const wallet = agentContext.wallet
assertAskarWallet(wallet)
const aesKey = await wallet.withSession((session) => session.fetchKey({ name: id }))

return Boolean(aesKey)
}

const aes128GcmGetKey =
(id: string) =>
async ({ agentContext }: { agentContext: AgentContext }) => {
const wallet = agentContext.wallet
assertAskarWallet(wallet)
const aesKey = await wallet.withSession((session) => session.fetchKey({ name: id }))

if (!aesKey) {
throw new Error(`AES-128-GCM key not found with id: ${id}`)
}

return aesKey.key
}

const aes128GcmEncrypt =
(id: string) =>
async ({
data,
agentContext,
nonce = new Uint8Array(12).fill(1),
}: {
data: Uint8Array
agentContext: AgentContext
nonce?: Uint8Array
}) => {
const wallet = agentContext.wallet
assertAskarWallet(wallet)

const key = await aes128GcmGetKey(id)({ agentContext })

const { ciphertextWithTag } = key.aeadEncrypt({ nonce, message: data })

return ciphertextWithTag
}

export const aes128Gcm = (id: string) => ({
aes128GcmGenerateAndStoreKey: aes128GcmGenerateAndStoreKey(id),
aes128GcmHasKey: aes128GcmHasKey(id),
aes128GcmEncrypt: aes128GcmEncrypt(id),
})
1 change: 1 addition & 0 deletions packages/agent/src/crypto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './aes'
1 change: 1 addition & 0 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export {
formatDifPexCredentialsForRequest,
} from './format/formatPresentation'
export * from './mediation'
export * from './crypto'
41 changes: 41 additions & 0 deletions packages/secure-store/kdf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Buffer } from '@credo-ts/core'
import argon2 from 'react-native-argon2'

/**
* Derive a hash from pin and salt. (which can be used to seed a key)
*
* Configuration based on recommended parameters defined in RFC 9106
* @see https://www.rfc-editor.org/rfc/rfc9106.html#name-parameter-choice
*
* returns a hex-encoded derived hash
*
*/
const derive = async (pin: string | Uint8Array, salt: string | Uint8Array): Promise<string> => {
const normalizedPin = typeof pin === 'string' ? pin : Buffer.from(pin).toString()
const normalizedSalt = typeof salt === 'string' ? salt : Buffer.from(salt).toString()
const { rawHash } = await argon2(normalizedPin, normalizedSalt, {
hashLength: 32,
mode: 'argon2id',
parallelism: 4,
iterations: 1,
memory: 21,
})

return rawHash
}

/**
* Generate 32 byte key crypto getRandomValues.
*
* @see https://github.com/LinusU/react-native-get-random-values
* @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
*/
const generateSalt = () => crypto.getRandomValues(new Uint8Array(32)).join('')

/**
* Derive key from pin and salt.
*
* Configuration based on recommended parameters defined in RFC 9106
* @see https://www.rfc-editor.org/rfc/rfc9106.html#name-parameter-choice
*/
export const kdf = { derive, generateSalt }
29 changes: 0 additions & 29 deletions packages/secure-store/secure-unlock/walletKeyDerivation.ts

This file was deleted.

6 changes: 3 additions & 3 deletions packages/secure-store/secureUnlock.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { WalletUnlockError } from './error/WalletUnlockError'
import { kdf } from './kdf'
import { getSalt, storeSalt } from './secure-unlock/saltStore'
import { deriveWalletKey, generateSalt } from './secure-unlock/walletKeyDerivation'
import {
canUseBiometryBackedWalletKey,
getWalletKeyUsingBiometrics,
Expand All @@ -25,7 +25,7 @@ export async function createSaltForPin(returnExisting = false) {
if (existingSalt) return existingSalt
}

const salt = generateSalt()
const salt = kdf.generateSalt()
await storeSalt(salt, version)

return salt
Expand All @@ -37,7 +37,7 @@ export async function getWalletKeyUsingPin(pin: string) {
throw new WalletUnlockError('Error unlocking wallet. No salt configured')
}

const walletKey = await deriveWalletKey(pin, salt)
const walletKey = await kdf.derive(pin, salt)
return walletKey
}

Expand Down
15 changes: 8 additions & 7 deletions packages/ui/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"extends": "../../tsconfig",
"include": ["src"],
"compilerOptions": {
"composite": true,
"jsx": "react-jsx"
},
"references": []
"extends": "../../tsconfig",
"include": ["src"],
"compilerOptions": {
"composite": true,
"jsx": "react-jsx",
"typeRoots": ["types"]
},
"references": []
}
Loading

0 comments on commit 7f1b4ad

Please sign in to comment.