Skip to content

Commit

Permalink
chore: sign using secp256k1 (#292)
Browse files Browse the repository at this point in the history
  • Loading branch information
hugoArregui authored Oct 3, 2023
1 parent fc0d31b commit 7222f3c
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 847 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@dcl/eslint-config": "^1.1.12",
"@dcl/protocol": "^1.0.0-6200210039.commit-75f18e8",
"@types/node": "^18.11.18",
"@types/secp256k1": "^4.0.4",
"@well-known-components/test-helpers": "^1.5.3",
"typescript": "^4.9.5"
},
Expand All @@ -27,7 +28,6 @@
"@dcl/catalyst-api-specs": "^3.2.3",
"@dcl/catalyst-contracts": "^4.2.0",
"@dcl/crypto": "^3.4.5",
"@dcl/hashing": "^3.0.4",
"@dcl/schemas": "^9.7.0",
"@dcl/urn-resolver": "^3.1.0",
"@well-known-components/env-config-provider": "^1.2.0",
Expand All @@ -38,8 +38,8 @@
"@well-known-components/thegraph-component": "^1.6.0",
"dcl-catalyst-client": "^21.5.5",
"eth-connect": "^6.2.1",
"eth-crypto": "^2.6.0",
"lru-cache": "^8.0.4"
"lru-cache": "^8.0.4",
"secp256k1": "^5.0.0"
},
"resolutions": {
"**/make-dir/semver": "^6.3.1"
Expand Down
18 changes: 0 additions & 18 deletions src/adapters/hasher.ts

This file was deleted.

32 changes: 20 additions & 12 deletions src/adapters/identity.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
import EthCrypto from 'eth-crypto'
import { IBaseComponent } from '@well-known-components/interfaces'
import { randomBytes } from 'crypto'
import secp256k1 from 'secp256k1'
import { sha3 } from 'eth-connect'

export type HashAndSignResult = {
hash: string
signedHash: string
}

export type IdentityComponent = IBaseComponent & {
getPublicKey(): string
getAddress(): string
sign(message: string): string
hashAndSign(message: string): HashAndSignResult
}

export function createIdentityComponent(): IdentityComponent {
const identity = EthCrypto.createIdentity()
let privKey: Uint8Array
do {
privKey = randomBytes(32)
} while (!secp256k1.privateKeyVerify(privKey))

function getAddress(): string {
return identity.address
}
const pubKey = secp256k1.publicKeyCreate(privKey)

function getPublicKey(): string {
return identity.publicKey
return Buffer.from(pubKey).toString('hex')
}

function sign(message: string): string {
return EthCrypto.sign(identity.privateKey, EthCrypto.hash.keccak256(message))
function hashAndSign(message: string): HashAndSignResult {
const hash = sha3(message)
const signedHash = Buffer.from(secp256k1.ecdsaSign(Buffer.from(hash, 'hex'), privKey).signature).toString('hex')
return { hash, signedHash }
}

return {
getAddress,
getPublicKey,
sign
hashAndSign
}
}
5 changes: 1 addition & 4 deletions src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import { createThirdPartyProvidersServiceFetcherComponent } from './adapters/thi
import { createThirdPartyProvidersStorage } from './logic/third-party-providers-storage'
import { createIdentityComponent } from './adapters/identity'
import { createProfilesComponent } from './adapters/profiles'
import { createHasherComponent } from './adapters/hasher'

// Initialize all the components of the app
export async function initComponents(
Expand Down Expand Up @@ -140,7 +139,6 @@ export async function initComponents(
namesFetcher
})

const hasher = createHasherComponent()
return {
config,
logs,
Expand Down Expand Up @@ -173,7 +171,6 @@ export async function initComponents(
poisFetcher,
nameDenylistFetcher,
identity,
profiles,
hasher
profiles
}
}
3 changes: 1 addition & 2 deletions src/controllers/handlers/about-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ export async function aboutHandler(
version: currentVersion,
commitHash: commitHash,
publicUrl: lambdasUrl.publicUrl,
publicKey: identity.getPublicKey(),
address: identity.getAddress()
publicKey: identity.getPublicKey()
},
configurations: {
networkId: contracts.chainId,
Expand Down
6 changes: 3 additions & 3 deletions src/controllers/handlers/profiles-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export async function profileHandler(
}

export async function explorerProfileHandler(
context: Pick<HandlerContextWithPath<'profiles' | 'identity' | 'hasher', '/profiles/:id'>, 'components' | 'params'>
context: Pick<HandlerContextWithPath<'profiles' | 'identity', '/profiles/:id'>, 'components' | 'params'>
): Promise<{ status: 200; body: any }> {
const { components, params } = context
const profile = await components.profiles.getProfile(params.id)
Expand All @@ -68,8 +68,8 @@ export async function explorerProfileHandler(
const avatar = profile.avatars[0]

const payload = JSON.stringify([avatar.name, avatar.hasClaimedName, ...avatar.avatar.wearables])
const hash = await components.hasher.hash(payload)
const signedHash = components.identity.sign(hash)

const { hash, signedHash } = components.identity.hashAndSign(payload)

return {
status: 200,
Expand Down
3 changes: 1 addition & 2 deletions src/controllers/handlers/status-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ export async function statusHandler(
version: version ?? '',
currentTime: Date.now(),
commitHash: commitHash ?? '',
publicKey: identity.getPublicKey(),
address: identity.getAddress()
publicKey: identity.getPublicKey()
}
}
}
2 changes: 0 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import { ThirdPartyProvidersGraphFetcher } from './adapters/third-party-provider
import { ThirdPartyProvidersStorage } from './logic/third-party-providers-storage'
import { IdentityComponent } from './adapters/identity'
import { IProfilesComponent } from './adapters/profiles'
import { HasherComponent } from './adapters/hasher'

export type GlobalContext = {
components: BaseComponents
Expand Down Expand Up @@ -76,7 +75,6 @@ export type BaseComponents = {
nameDenylistFetcher: NameDenylistFetcher
identity: IdentityComponent
profiles: IProfilesComponent
hasher: HasherComponent
}

// components used in runtime
Expand Down
7 changes: 4 additions & 3 deletions test/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createIdentity } from 'eth-crypto'
import { computeAddress } from '@dcl/crypto/dist/crypto'
import { createIdentityComponent } from '../src/adapters/identity'

export function generateRandomAddress(): string {
const identity = createIdentity()
return identity.address
const pubKey = createIdentityComponent().getPublicKey()
return computeAddress(Buffer.from(pubKey, 'hex'))
}
29 changes: 12 additions & 17 deletions test/unit/profile-handlers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import { generateRandomAddress } from '../helpers'
import { profileEntityFullWithExtendedItems } from '../integration/data/profiles-responses'
import { Request } from 'node-fetch'
import { createIdentityComponent } from '../../src/adapters/identity'
import { createHasherComponent } from '../../src/adapters/hasher'
import EthCrypto from 'eth-crypto'
import { hashV1 } from '@dcl/hashing'
import secp256k1 from 'secp256k1'
import { sha3 } from 'eth-connect'

const profile = { timestamp: Date.now(), ...profileEntityFullWithExtendedItems.metadata }

Expand Down Expand Up @@ -159,11 +158,7 @@ describe('GET /explorer/profiles/{id} handler unit test', () => {
},
identity: {
getPublicKey: () => 'public_key',
getAddress: () => 'address',
sign: jest.fn().mockImplementation(() => 'signed')
},
hasher: {
hash: jest.fn().mockImplementation(() => 'hash')
hashAndSign: jest.fn().mockImplementation(() => ({ hash: 'hash', signedHash: 'signed' }))
}
}
}
Expand All @@ -183,10 +178,9 @@ describe('GET /explorer/profiles/{id} handler unit test', () => {
const avatar = profile.avatars[0]
expect(components.profiles.getProfiles).not.toHaveBeenCalled()
expect(components.profiles.getProfile).toHaveBeenCalledWith(address)
expect(components.hasher.hash).toHaveBeenCalledWith(
expect(components.identity.hashAndSign).toHaveBeenCalledWith(
JSON.stringify([avatar.name, avatar.hasClaimedName, ...avatar.avatar.wearables])
)
expect(components.identity.sign).toHaveBeenCalledWith('hash')

expect(status).toEqual(200)
expect(body.profile).toEqual(profile)
Expand All @@ -200,7 +194,6 @@ describe('GET /explorer/profiles/{id} handler unit test', () => {
getProfiles: jest.fn().mockImplementation(async () => undefined),
getProfile: jest.fn().mockImplementation(async () => profile)
},
hasher: createHasherComponent(),
identity: createIdentityComponent()
}
const { status, body } = await explorerProfileHandler({ components, params: { id: address } })
Expand All @@ -214,12 +207,14 @@ describe('GET /explorer/profiles/{id} handler unit test', () => {
expect(body.hash).toBeTruthy()
expect(body.signedHash).toBeTruthy()

const encoder = new TextEncoder()

const payload = JSON.stringify([avatar.name, avatar.hasClaimedName, ...avatar.avatar.wearables])
expect(await hashV1(encoder.encode(payload))).toEqual(body.hash)
expect(EthCrypto.recover(body.signedHash, EthCrypto.hash.keccak256(body.hash))).toEqual(
components.identity.getAddress()
)
expect(sha3(payload)).toEqual(body.hash)
expect(
secp256k1.ecdsaVerify(
Buffer.from(body.signedHash, 'hex'),
Buffer.from(body.hash, 'hex'),
Buffer.from(components.identity.getPublicKey(), 'hex')
)
).toBeTruthy()
})
})
Loading

0 comments on commit 7222f3c

Please sign in to comment.