Skip to content

Commit

Permalink
refactor: move encryption/decryption of pks responsibility to dao (#998)
Browse files Browse the repository at this point in the history
  • Loading branch information
lautarodragan committed Sep 21, 2019
1 parent 6d7f73b commit 98bac5e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 28 deletions.
6 changes: 1 addition & 5 deletions src/Frost.ts
Expand Up @@ -42,7 +42,7 @@ export async function Frost(localVars: any = {}) {
const mongoClient = await MongoClient.connect(configuration.mongodbUrl)
const dbConnection = await mongoClient.db()
const accountCollection = dbConnection.collection('accounts')
const accountDao = AccountDao(accountCollection)
const accountDao = AccountDao(accountCollection, configuration.privateKeyEncryptionKey)

const sendEmail = SendEmail({
nodemailer: {
Expand Down Expand Up @@ -78,14 +78,10 @@ export async function Frost(localVars: any = {}) {
configuration: {
verifiedAccount: configuration.verifiedAccount,
jwtSecret: configuration.jwt,
privateKeyEncryptionKey: configuration.privateKeyEncryptionKey,
},
})

const workController = WorkController({
configuration: {
privateKeyEncryptionKey: configuration.privateKeyEncryptionKey,
},
dependencies: {
logger: logger.child({ file: 'WorkController' }),
mainnetNode,
Expand Down
5 changes: 1 addition & 4 deletions src/controllers/AccountController.ts
Expand Up @@ -18,7 +18,6 @@ import {
Unauthorized,
} from '../errors/errors'
import { PasswordHelper } from '../helpers/Password'
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 @@ -76,7 +75,6 @@ interface Dependencies {
interface Configuration {
readonly verifiedAccount: boolean
readonly jwtSecret: string
readonly privateKeyEncryptionKey: string
}

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

const id = await getUnusedId()
const { privateKey, publicKey } = generateED25519Base58Keys()
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 All @@ -143,7 +140,7 @@ export const AccountController = ({
email,
emailPublic: false,
password: hashedPassword,
privateKey: encryptedPrivateKey,
privateKey,
publicKey,
createdAt: Date.now().toString(), // .toString(): legacy reasons
verified: configuration.verifiedAccount,
Expand Down
13 changes: 1 addition & 12 deletions src/controllers/WorkController.ts
Expand Up @@ -7,7 +7,6 @@ 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'

export interface WorkController {
Expand All @@ -23,14 +22,9 @@ 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 @@ -45,9 +39,6 @@ export const WorkController = ({
testnetNode,
verifiableClaimSigner,
},
configuration: {
privateKeyEncryptionKey,
},
}: Arguments): WorkController => {
const networkToNode = (network: Network) => network === Network.LIVE ? mainnetNode : testnetNode

Expand All @@ -61,7 +52,7 @@ export const WorkController = ({
return node.searchWorks(filters)
}

const create = async (claim: any, context: any, issuer: string, encryptedPrivateKey: string, network: Network) => {
const create = async (claim: any, context: any, issuer: string, privateKey: string, network: Network) => {
const node = networkToNode(network)

const legacyContext = {
Expand All @@ -75,8 +66,6 @@ export const WorkController = ({
},
}

const privateKey = decrypt(encryptedPrivateKey, privateKeyEncryptionKey)

const createAndSignClaim = pipeP(
configureCreateVerifiableClaim({ issuer, context: { ...legacyContext, ...context, ...aboutContext} }),
verifiableClaimSigner.configureSignVerifiableClaim({ privateKey }),
Expand Down
43 changes: 36 additions & 7 deletions src/daos/AccountDao.ts
@@ -1,5 +1,7 @@
import { Collection, Binary } from 'mongodb'
import { pipe } from 'ramda'

import { encrypt, decrypt } from '../helpers/crypto'
import { bytesToUuid, uuidToBytes } from '../helpers/uuid'
import { Network } from '../interfaces/Network'
import { Account } from '../models/Account'
Expand All @@ -12,31 +14,34 @@ export interface AccountDao {
insertToken: (filter: Partial<Account>, network: Network, token: string) => Promise<void>
}

export const AccountDao = (collection: Collection): AccountDao => {
export const AccountDao = (collection: Collection, encryptionKey: string): AccountDao => {
const modelToEncryptedDocument = pipe(encryptAccount(encryptionKey), modelToDocument)
const documentToDecryptedModel = pipe(documentToModel, decryptAccount(encryptionKey))

const createIndices = async () => {
await collection.createIndex({ email: 1 }, { unique: true })
await collection.createIndex({ id: 1 }, { unique: false })
}

const insertOne = async (account: Account): Promise<void> => {
const document = modelToDocument(account)
const document = modelToEncryptedDocument(account)
await collection.insertOne(document)
}

const findOne = async (filter: Partial<Account>): Promise<Account> => {
const document = modelToDocument(filter)
const document = modelToEncryptedDocument(filter)
const account: AccountDocument = await collection.findOne(document)
return account && documentToModel(account) as Account
return account && documentToDecryptedModel(account) as Account
}

const updateOne = async (filter: Partial<Account>, updates: Partial<Account>): Promise<void> => {
const filterDocument = modelToDocument(filter)
const updatesDocument = modelToDocument(updates)
const filterDocument = modelToEncryptedDocument(filter)
const updatesDocument = modelToEncryptedDocument(updates)
await collection.updateOne(filterDocument, { $set: updatesDocument })
}

const insertToken = async (filter: Partial<Account>, network: Network, token: string): Promise<void> => {
const filterDocument = modelToDocument(filter)
const filterDocument = modelToEncryptedDocument(filter)
const array = network === Network.LIVE ? 'apiTokens' : 'testApiTokens'
await collection.updateOne(filterDocument, { $push: { [array]: { token } }})
}
Expand Down Expand Up @@ -81,6 +86,30 @@ const modelToDocument = (model: Partial<Account>): Partial<AccountDocument> => {
return document
}

const encryptAccount = (encryptionKey: string) => (account: Partial<Account>): Partial<Account> => {
const encryptedAccount = {
...account,
privateKey: account.privateKey && encrypt(account.privateKey, encryptionKey),
}

if (!account.privateKey)
delete encryptedAccount.privateKey

return encryptedAccount
}

const decryptAccount = (decryptionKey: string) => (account: Partial<Account>): Partial<Account> => {
const decryptedAccount = {
...account,
privateKey: account.privateKey && decrypt(account.privateKey, decryptionKey),
}

if (!account.privateKey)
delete decryptedAccount.privateKey

return decryptedAccount
}

interface AccountDocument {
readonly id?: Buffer | Binary
readonly email: string
Expand Down

0 comments on commit 98bac5e

Please sign in to comment.