Skip to content

Commit

Permalink
refactor: use crypto module for private key encryption (#990)
Browse files Browse the repository at this point in the history
  • Loading branch information
lautarodragan authored Sep 12, 2019
1 parent 03d0c10 commit 9b36e82
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 6 deletions.
19 changes: 16 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"@types/koa-router": "7.0.35",
"@types/mocha": "5.2.5",
"@types/mongodb": "3.1.17",
"@types/node": "10.12.15",
"@types/node": "12.7.5",
"@types/node-fetch": "2.1.4",
"@types/node-vault": "0.5.3",
"@types/pino": "5.8.3",
Expand Down
4 changes: 4 additions & 0 deletions src/Frost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,14 @@ export async function Frost(localVars: any = {}) {
verifiedAccount: configuration.verifiedAccount,
pwnedCheckerRoot: configuration.pwnedCheckerRoot,
jwtSecret: configuration.jwt,
privateKeyEncryptionKey: configuration.privateKeyEncryptionKey,
},
})

const workController = WorkController({
configuration: {
privateKeyEncryptionKey: configuration.privateKeyEncryptionKey,
},
dependencies: {
logger: logger.child({ file: 'WorkController' }),
mainnetNode,
Expand Down
2 changes: 2 additions & 0 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface Configuration
readonly poeContractAddress: string
readonly poeBalanceMinimum: number
readonly maximumFileSizeInBytes: number
readonly privateKeyEncryptionKey: string
}

export const configuration: Configuration = {
Expand Down Expand Up @@ -118,4 +119,5 @@ export const configuration: Configuration = {
poeContractDecimals: 8,
poeBalanceMinimum: 1000,
maximumFileSizeInBytes: MiB * 15,
privateKeyEncryptionKey: '0000000000000000000000000000000000000000000000000000000000000000',
}
4 changes: 3 additions & 1 deletion src/controllers/AccountController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ResourceNotFound,
Unauthorized,
} from '../errors/errors'
import { encrypt } from '../helpers/crypto'
import { tokenMatch } from '../helpers/token'
import { uuid4 } from '../helpers/uuid'
import { isJWTData, JWTData } from '../interfaces/JWTData'
Expand Down Expand Up @@ -74,6 +75,7 @@ interface Configuration {
readonly verifiedAccount: boolean
readonly pwnedCheckerRoot: string
readonly jwtSecret: string
readonly privateKeyEncryptionKey: string
}

interface Arguments {
Expand Down Expand Up @@ -128,7 +130,7 @@ export const AccountController = ({

const id = await getUnusedId()
const { privateKey, publicKey } = generateED25519Base58Keys()
const encryptedPrivateKey = await Vault.encrypt(privateKey)
const encryptedPrivateKey = encrypt(privateKey, configuration.privateKeyEncryptionKey)
const apiToken = await createJWT({ accountId: id, network: Network.TEST }, Token.TestApiKey)
const encryptedToken = await Vault.encrypt(`TEST_${apiToken}`)
const issuer = createIssuerFromPrivateKey(privateKey)
Expand Down
13 changes: 12 additions & 1 deletion src/controllers/WorkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as Pino from 'pino'
import { pipeP } from 'ramda'

import { PoetNode, WorkSearchFilters } from '../daos/PoetNodeDao'
import { decrypt } from '../helpers/crypto'
import { Network } from '../interfaces/Network'
import { Vault } from '../utils/Vault/Vault'

Expand All @@ -23,9 +24,14 @@ export interface WorkController {
}

interface Arguments {
readonly configuration: Configuration
readonly dependencies: Dependencies
}

interface Configuration {
readonly privateKeyEncryptionKey: string
}

interface Dependencies {
readonly logger: Pino.Logger
readonly mainnetNode: PoetNode
Expand All @@ -40,6 +46,9 @@ export const WorkController = ({
testnetNode,
verifiableClaimSigner,
},
configuration: {
privateKeyEncryptionKey,
},
}: Arguments): WorkController => {
const networkToNode = (network: Network) => network === Network.LIVE ? mainnetNode : testnetNode

Expand Down Expand Up @@ -67,7 +76,9 @@ export const WorkController = ({
},
}

const privateKey = await Vault.decrypt(encryptedPrivateKey)
const privateKey = encryptedPrivateKey.startsWith('vault')
? await Vault.decrypt(encryptedPrivateKey)
: decrypt(encryptedPrivateKey, privateKeyEncryptionKey)

const createAndSignClaim = pipeP(
configureCreateVerifiableClaim({ issuer, context: { ...legacyContext, ...context, ...aboutContext} }),
Expand Down
19 changes: 19 additions & 0 deletions src/helpers/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'

const algorithm = 'id-aes256-GCM'
const ivLengthInBytes = 96

export const encrypt = (text: string, key: string): string => {
const iv = randomBytes(ivLengthInBytes)
const cipher = createCipheriv(algorithm, Buffer.from(key, 'hex'), iv)
const ciphertext = cipher.update(text, 'utf8', 'hex') + cipher.final('hex')
const authTag = cipher.getAuthTag().toString('hex')
return ciphertext + '|' + authTag + '|' + iv.toString('hex')
}

export const decrypt = (ciphertextWithAuthTagAndIv: string, key: string): string => {
const [ciphertext, authTagHex, ivHex] = ciphertextWithAuthTagAndIv.split('|')
const decipher = createDecipheriv(algorithm, Buffer.from(key, 'hex'), Buffer.from(ivHex, 'hex'))
decipher.setAuthTag(Buffer.from(authTagHex, 'hex'))
return decipher.update(ciphertext, 'hex').toString('utf8') + decipher.final('utf8')
}
8 changes: 8 additions & 0 deletions typings/node.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
declare module 'crypto' {
interface Cipher {
getAuthTag: () => Buffer
}
interface Decipher {
setAuthTag: (authTag: Buffer) => void
}
}

0 comments on commit 9b36e82

Please sign in to comment.