Skip to content

Commit

Permalink
feat: add derive shared secret with hkdf
Browse files Browse the repository at this point in the history
  • Loading branch information
xstelea committed Jun 19, 2024
1 parent 8ab56ef commit 15b8145
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { describe, expect, it } from 'vitest'
import { Curve25519 } from './curve25519'

const keyPair1 = Curve25519(
'4181137e23935d9b0e2bc39a798817df6bdddaab415d604801ef76b412f48124',
)
const keyPair2 = Curve25519(
'c1c117c059232c18263704a500f2e800d5c8e8f63ef7719e9b033602488644dd',
)

const dAppDefinitionAddress =
'account_tdx_2_12yf9gd53yfep7a669fv2t3wm7nz9zeezwd04n02a433ker8vza6rhe'

describe('Curve25519', () => {
it('should generate a key pair', () => {
expect(keyPair1.getPrivateKey()).toBeDefined()
expect(keyPair1.x25519.getPublicKey()).toBeDefined()
expect(keyPair1.ed25519.getPublicKey()).toBeDefined()
expect(keyPair2.getPrivateKey()).toBeDefined()
expect(keyPair2.x25519.getPublicKey()).toBeDefined()
expect(keyPair2.ed25519.getPublicKey()).toBeDefined()
})
it.only('should calculate a shared secret', () => {
const sharedSecretResult = keyPair1.x25519.calculateSharedSecret(
keyPair2.x25519.getPublicKey(),
dAppDefinitionAddress,
)

if (sharedSecretResult.isErr()) throw sharedSecretResult.error

console.log({
sharedSecretResult: sharedSecretResult.value,
keyPair1PrivateKeyKey: keyPair2.x25519.getPublicKey(),
keyPair2PublicKey: keyPair2.x25519.getPublicKey(),
})

expect(sharedSecretResult.value).toBe(
'e9278143a272e9cce596335d0f29e0194305a2a00b57501bc4c21a432d5c2b49264231657e186b6e70a13da4bbf23be6',
)
})
})
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { x25519, ed25519 } from '@noble/curves/ed25519'
import { Buffer } from 'buffer'
import { Result, err, ok } from 'neverthrow'
import { hkdf } from '@noble/hashes/hkdf'
import { sha256 } from '@noble/hashes/sha256'

const toHex = (input: Uint8Array) => Buffer.from(input).toString('hex')

export type KeyPairProvider = (privateKeyHex?: string) => {
getPrivateKey: () => string
x25519: {
getPublicKey: () => string
calculateSharedSecret: (publicKeyHex: string) => Result<string, Error>
calculateSharedSecret: (
publicKeyHex: string,
dAppDefinitionAddress: string,
) => Result<string, Error>
}
ed25519: {
getPublicKey: () => string
Expand All @@ -24,9 +29,20 @@ export const Curve25519: KeyPairProvider = (
const getPrivateKey = () => privateKeyHex
const x25519Api = {
getPublicKey: () => toHex(x25519.getPublicKey(privateKeyHex)),
calculateSharedSecret: (publicKeyHex: string): Result<string, Error> => {
calculateSharedSecret: (
publicKeyHex: string,
dAppDefinitionAddress: string,
): Result<string, Error> => {
try {
return ok(toHex(x25519.getSharedSecret(privateKeyHex, publicKeyHex)))
const sharedSecret = x25519.getSharedSecret(privateKeyHex, publicKeyHex)
const derived = hkdf(
sha256,
sharedSecret,
Buffer.from(dAppDefinitionAddress, 'utf-8'),
'RCfM',
48,
)
return ok(toHex(derived))
} catch (error) {
return err(error as Error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type IdentityStore = {
export type IdentityModule = ReturnType<typeof IdentityModule>
export const IdentityModule = (input: {
logger?: Logger
dAppDefinitionAddress: string
providers: {
storageModule: StorageModule<IdentitySecret>
KeyPairModule: KeyPairProvider
Expand Down Expand Up @@ -57,9 +58,11 @@ export const IdentityModule = (input: {
.mapErr(() => ({ reason: 'couldNotDeriveSharedSecret' }))
.andThen((identity) =>
identity
? identity.x25519.calculateSharedSecret(publicKey).mapErr(() => ({
reason: 'FailedToDeriveSharedSecret',
}))
? identity.x25519
.calculateSharedSecret(publicKey, input.dAppDefinitionAddress)
.mapErr(() => ({
reason: 'FailedToDeriveSharedSecret',
}))
: err({ reason: 'DappIdentityNotFound' }),
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ResultAsync, err, errAsync, ok } from 'neverthrow'
import { Subject, Subscription, share } from 'rxjs'
import { Subscription } from 'rxjs'
import { EncryptionModule, transformBufferToSealbox } from '../../encryption'
import { Session, SessionModule } from '../../session/session.module'
import type {
Expand All @@ -13,12 +13,11 @@ import { DeepLinkModule } from './deep-link.module'
import { IdentityModule } from '../../identity/identity.module'
import { RequestItemModule } from '../../request-items/request-item.module'
import { StorageModule } from '../../../storage'
import { Curve25519, KeyPairProvider } from '../../crypto'
import { Curve25519 } from '../../crypto'
import {
RadixConnectRelayApiService,
WalletResponse,
} from './radix-connect-relay-api.service'
import { RequestItem } from 'radix-connect-common'
import type { TransportProvider } from '../../../../_types'
import { base64urlEncode } from './helpers/base64url'

Expand All @@ -27,6 +26,7 @@ export const RadixConnectRelayModule = (input: {
baseUrl: string
logger?: Logger
walletUrl: string
dAppDefinitionAddress: string
providers: {
requestItemModule: RequestItemModule
storageModule: StorageModule
Expand All @@ -53,6 +53,7 @@ export const RadixConnectRelayModule = (input: {
providers?.identityModule ??
IdentityModule({
logger,
dAppDefinitionAddress: input.dAppDefinitionAddress,
providers: {
storageModule: storageModule.getPartition('identities'),
KeyPairModule: Curve25519,
Expand Down Expand Up @@ -221,7 +222,10 @@ export const RadixConnectRelayModule = (input: {
return errAsync({ reason: walletResponse.error })
}
return dAppIdentity.x25519
.calculateSharedSecret(walletResponse.publicKey)
.calculateSharedSecret(
walletResponse.publicKey,
input.dAppDefinitionAddress,
)
.mapErr(() => ({ reason: 'FailedToDeriveSharedSecret' }))
.asyncAndThen((sharedSecret) =>
decryptWalletResponseData(sharedSecret, walletResponse.data),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export const WalletRequestModule = (input: {
logger,
walletUrl: 'radixWallet://',
baseUrl: 'https://radix-connect-relay.radixdlt.com',
dAppDefinitionAddress: input.dAppDefinitionAddress,
providers: {
requestItemModule,
storageModule,
Expand Down

0 comments on commit 15b8145

Please sign in to comment.