Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pref: Migrated from RSA to ECC #139

Merged
merged 2 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions apps/api/src/common/create-key-pair.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { generateKeyPairSync } from 'crypto'
import eccrypto from 'eccrypto'

export const createKeyPair = (): {
publicKey: string
privateKey: string
} => {
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048
})

const publicKeyPEM = publicKey.export({ type: 'pkcs1', format: 'pem' })
const privateKeyPEM = privateKey.export({ type: 'pkcs1', format: 'pem' })
const privateKey = eccrypto.generatePrivate()
const publicKey = eccrypto.getPublic(privateKey)

Check warning on line 8 in apps/api/src/common/create-key-pair.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/create-key-pair.ts#L7-L8

Added lines #L7 - L8 were not covered by tests

return {
publicKey: publicKeyPEM.toString(),
privateKey: privateKeyPEM.toString()
publicKey: publicKey.toString('hex'),
privateKey: privateKey.toString('hex')
}
}
28 changes: 19 additions & 9 deletions apps/api/src/common/decrypt.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { createPrivateKey, privateDecrypt } from 'crypto'
import eccrypto from 'eccrypto'

export const decrypt = (privateKey: string, data: string): string => {
const privateKeyPEM = createPrivateKey({
key: privateKey,
format: 'pem'
})
export const decrypt = async (
privateKey: string,
data: string
): Promise<string> => {
const parsed = JSON.parse(data)

Check warning on line 7 in apps/api/src/common/decrypt.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/decrypt.ts#L6-L7

Added lines #L6 - L7 were not covered by tests

const buffer = Buffer.from(data, 'base64')
const decrypted = privateDecrypt(privateKeyPEM, buffer)
return decrypted.toString('utf8')
const eicesData = {

Check warning on line 9 in apps/api/src/common/decrypt.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/decrypt.ts#L9

Added line #L9 was not covered by tests
iv: Buffer.from(parsed.iv),
ephemPublicKey: Buffer.from(parsed.ephemPublicKey),
ciphertext: Buffer.from(parsed.ciphertext),
mac: Buffer.from(parsed.mac)
}

const decrypted = await eccrypto.decrypt(

Check warning on line 16 in apps/api/src/common/decrypt.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/decrypt.ts#L16

Added line #L16 was not covered by tests
Buffer.from(privateKey, 'hex'),
eicesData
)

return decrypted.toString()

Check warning on line 21 in apps/api/src/common/decrypt.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/decrypt.ts#L21

Added line #L21 was not covered by tests
}
19 changes: 10 additions & 9 deletions apps/api/src/common/encrypt.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { createPublicKey, publicEncrypt } from 'crypto'
import eccrypto from 'eccrypto'

export const encrypt = (publicKey: string, data: string): string => {
const publicKeyPEM = createPublicKey({
key: publicKey,
format: 'pem'
})
export const encrypt = async (
publicKey: string,
data: string
): Promise<string> => {
const encrypted = await eccrypto.encrypt(

Check warning on line 7 in apps/api/src/common/encrypt.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/encrypt.ts#L6-L7

Added lines #L6 - L7 were not covered by tests
Buffer.from(publicKey, 'hex'),
Buffer.from(data)
)

const messageBuffer = Buffer.from(data)
const encrypted = publicEncrypt(publicKeyPEM, messageBuffer)
return encrypted.toString('base64')
return JSON.stringify(encrypted)

Check warning on line 12 in apps/api/src/common/encrypt.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/common/encrypt.ts#L12

Added line #L12 was not covered by tests
}
20 changes: 11 additions & 9 deletions apps/api/src/common/util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,25 @@ describe('util', () => {
expect(keyPair.privateKey).toBeDefined()
})

it('should encrypt and decrypt a string', () => {
it('should encrypt and decrypt a string', async () => {
const keyPair = createKeyPair()
const plaintext = 'hello world'
const encrypted = encrypt(keyPair.publicKey, plaintext)
const decrypted = decrypt(keyPair.privateKey, encrypted)
const encrypted = await encrypt(keyPair.publicKey, plaintext)
const decrypted = await decrypt(keyPair.privateKey, encrypted)
expect(decrypted).toEqual(plaintext)
})

it('should fail to encrypt and decrypt a string', () => {
it('should fail to encrypt and decrypt a string', async () => {
const keyPair = createKeyPair()
const differenetKeyPair = createKeyPair()
const plainText = 'hello world'
const encrypted = encrypt(keyPair.publicKey, plainText)
const decrypted = () => {
decrypt(differenetKeyPair.privateKey, encrypted);
};
expect(decrypted).toThrow()
const encrypted = await encrypt(keyPair.publicKey, plainText)
try {
await decrypt(differenetKeyPair.privateKey, encrypted)
} catch (e) {
expect(e).toBeDefined()
expect(e.message).toEqual('Bad MAC')
}
})

it('should exclude fields', () => {
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/project/service/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
dto: CreateProject
): Promise<Project> {
// Check if the workspace exists or not
const workspace = await getWorkspaceWithAuthority(

Check warning on line 35 in apps/api/src/project/service/project.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/project/service/project.service.ts#L35

Added line #L35 was not covered by tests
user.id,
workspaceId,
Authority.CREATE_SECRET,
Expand Down Expand Up @@ -158,7 +158,7 @@
...createEnvironmentOps
])

createEvent(

Check warning on line 161 in apps/api/src/project/service/project.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/project/service/project.service.ts#L161

Added line #L161 was not covered by tests
{
triggeredBy: user,
entity: newProject,
Expand Down Expand Up @@ -241,8 +241,8 @@
for (const version of versions) {
updatedVersions.push({
id: version.id,
value: encrypt(
decrypt(project.privateKey, version.value),
value: await encrypt(
await decrypt(project.privateKey, version.value),
privateKey
)
})
Expand Down Expand Up @@ -279,7 +279,7 @@
...versionUpdateOps
])

createEvent(

Check warning on line 282 in apps/api/src/project/service/project.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/project/service/project.service.ts#L282

Added line #L282 was not covered by tests
{
triggeredBy: user,
entity: updatedProject,
Expand Down Expand Up @@ -316,7 +316,7 @@
}
})

createEvent(

Check warning on line 319 in apps/api/src/project/service/project.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/project/service/project.service.ts#L319

Added line #L319 was not covered by tests
{
triggeredBy: user,
entity: project,
Expand Down
10 changes: 5 additions & 5 deletions apps/api/src/secret/service/secret.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@
}

// Create the secret
const secret = await this.prisma.secret.create({

Check warning on line 75 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L75

Added line #L75 was not covered by tests
data: {
name: dto.name,
rotateAt: addHoursToDate(dto.rotateAfter),
versions: {
create: {
value: encrypt(project.publicKey, dto.value),
value: await encrypt(project.publicKey, dto.value),
version: 1,
createdById: user.id
}
Expand All @@ -101,7 +101,7 @@
}
})

createEvent(

Check warning on line 104 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L104

Added line #L104 was not covered by tests
{
triggeredBy: user,
entity: secret,
Expand All @@ -120,13 +120,13 @@
this.prisma
)

this.logger.log(`User ${user.id} created secret ${secret.id}`)

Check warning on line 123 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L123

Added line #L123 was not covered by tests

return secret

Check warning on line 125 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L125

Added line #L125 was not covered by tests
}

async updateSecret(user: User, secretId: Secret['id'], dto: UpdateSecret) {
const secret = await getSecretWithAuthority(

Check warning on line 129 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L129

Added line #L129 was not covered by tests
user.id,
secretId,
Authority.UPDATE_SECRET,
Expand Down Expand Up @@ -160,7 +160,7 @@
take: 1
})

result = await this.prisma.secret.update({

Check warning on line 163 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L163

Added line #L163 was not covered by tests
where: {
id: secretId
},
Expand All @@ -170,7 +170,7 @@
lastUpdatedById: user.id,
versions: {
create: {
value: encrypt(secret.project.publicKey, dto.value),
value: await encrypt(secret.project.publicKey, dto.value),
version: previousVersion.version + 1,
createdById: user.id
}
Expand All @@ -179,7 +179,7 @@
})
}

result = await this.prisma.secret.update({

Check warning on line 182 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L182

Added line #L182 was not covered by tests
where: {
id: secretId
},
Expand All @@ -190,7 +190,7 @@
}
})

createEvent(

Check warning on line 193 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L193

Added line #L193 was not covered by tests
{
triggeredBy: user,
entity: secret,
Expand All @@ -207,9 +207,9 @@
this.prisma
)

this.logger.log(`User ${user.id} updated secret ${secret.id}`)

Check warning on line 210 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L210

Added line #L210 was not covered by tests

return result

Check warning on line 212 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L212

Added line #L212 was not covered by tests
}

async updateSecretEnvironment(
Expand All @@ -217,7 +217,7 @@
secretId: Secret['id'],
environmentId: Environment['id']
) {
const secret = await getSecretWithAuthority(

Check warning on line 220 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L220

Added line #L220 was not covered by tests
user.id,
secretId,
Authority.UPDATE_SECRET,
Expand All @@ -243,7 +243,7 @@
}

// Update the secret
const result = await this.prisma.secret.update({

Check warning on line 246 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L246

Added line #L246 was not covered by tests
where: {
id: secretId
},
Expand All @@ -252,7 +252,7 @@
}
})

createEvent(

Check warning on line 255 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L255

Added line #L255 was not covered by tests
{
triggeredBy: user,
entity: secret,
Expand All @@ -271,9 +271,9 @@
this.prisma
)

this.logger.log(`User ${user.id} updated secret ${secret.id}`)

Check warning on line 274 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L274

Added line #L274 was not covered by tests

return result

Check warning on line 276 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L276

Added line #L276 was not covered by tests
}

async rollbackSecret(
Expand All @@ -282,7 +282,7 @@
rollbackVersion: SecretVersion['version']
) {
// Fetch the secret
const secret = await getSecretWithAuthority(

Check warning on line 285 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L285

Added line #L285 was not covered by tests
user.id,
secretId,
Authority.UPDATE_SECRET,
Expand All @@ -309,7 +309,7 @@

async deleteSecret(user: User, secretId: Secret['id']) {
// Check if the user has the required role
await getSecretWithAuthority(

Check warning on line 312 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L312

Added line #L312 was not covered by tests
user.id,
secretId,
Authority.DELETE_SECRET,
Expand All @@ -330,7 +330,7 @@
decryptValue: boolean
) {
// Fetch the secret
const secret = await getSecretWithAuthority(

Check warning on line 333 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L333

Added line #L333 was not covered by tests
user.id,
secretId,
Authority.READ_SECRET,
Expand All @@ -356,7 +356,7 @@

if (decryptValue) {
// Decrypt the secret value
const decryptedValue = decrypt(
const decryptedValue = await decrypt(

Check warning on line 359 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L359

Added line #L359 was not covered by tests
project.privateKey,
secret.versions[0].value
)
Expand All @@ -369,7 +369,7 @@

async getAllVersionsOfSecret(user: User, secretId: Secret['id']) {
// Fetch the secret
const secret = await getSecretWithAuthority(

Check warning on line 372 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L372

Added line #L372 was not covered by tests
user.id,
secretId,
Authority.READ_SECRET,
Expand Down Expand Up @@ -447,10 +447,10 @@
})) as SecretWithVersion[]

// Return the secrets
return secrets.map((secret) => {
return secrets.map(async (secret) => {

Check warning on line 450 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L450

Added line #L450 was not covered by tests
if (decryptValue) {
// Decrypt the secret value
const decryptedValue = decrypt(
const decryptedValue = await decrypt(

Check warning on line 453 in apps/api/src/secret/service/secret.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/api/src/secret/service/secret.service.ts#L453

Added line #L453 was not covered by tests
project.privateKey,
secret.versions[0].value
)
Expand Down
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,13 @@
"ts-jest": "^29.1.1",
"ts-node": "10.9.1",
"typescript": "~5.2.2",
"url-loader": "^4.1.1"
"url-loader": "^4.1.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^11.1.0",
"@semantic-release/git": "^10.0.1",
"@semantic-release/release-notes-generator": "^12.1.0",
"@types/jsonwebtoken": "^9.0.5",
"@types/eccrypto": "^1.1.6"
},
"dependencies": {
"@nestjs/common": "^10.3.0",
Expand All @@ -177,18 +183,13 @@
"@nestjs/schedule": "^4.0.0",
"@nestjs/swagger": "^7.1.17",
"@prisma/client": "^5.7.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^11.1.0",
"@semantic-release/git": "^10.0.1",
"@semantic-release/release-notes-generator": "^12.1.0",
"@supabase/supabase-js": "^2.39.2",
"@types/jsonwebtoken": "^9.0.5",
"axios": "^1.6.5",
"chalk": "4.1.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"conventional-changelog-conventionalcommits": "^7.0.2",
"dotenv": "^16.3.1",
"eccrypto": "^1.1.6",
"express": "^4.18.2",
"husky": "^8.0.3",
"jest-mock-extended": "^3.0.5",
Expand Down
Loading