Skip to content
This repository was archived by the owner on May 19, 2023. It is now read-only.

Commit 70c8c98

Browse files
authored
Alternative encryption (#73)
* Change auth manager name * Alternative encryption algorithm * Lint * Fix test import * Change iv type * Prvent encode/decode from hex * Bump to beta 10
1 parent 033523e commit 70c8c98

19 files changed

+34690
-76
lines changed

modules/ipfs-cpinner-client/package-lock.json

Lines changed: 10220 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/ipfs-cpinner-client/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rsksmart/ipfs-cpinner-client",
3-
"version": "0.1.1-beta.9",
3+
"version": "0.1.1-beta.10",
44
"description": "RIF Data Vault - IPFS centralized pinner client",
55
"main": "dist/bundle.js",
66
"types": "lib/index.d.ts",
@@ -47,6 +47,7 @@
4747
"dependencies": {
4848
"axios": "^0.21.1",
4949
"buffer": "^6.0.3",
50+
"crypto-js": "^4.0.0",
5051
"did-jwt": "^4.6.2",
5152
"eth-sig-util": "^3.0.0"
5253
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import HmacSHA256 from 'crypto-js/hmac-sha256'
2+
import HmacSHA512 from 'crypto-js/hmac-sha512'
3+
import AES from 'crypto-js/aes'
4+
import encUtf8 from 'crypto-js/enc-utf8'
5+
import encHex from 'crypto-js/enc-hex'
6+
import ModeCTR from 'crypto-js/mode-ctr'
7+
import WordArray from 'crypto-js/lib-typedarrays'
8+
import { Web3Provider } from '../web3provider/types'
9+
10+
export const generateKeyViaRPC = (provider: Web3Provider, account: string) => provider.request({
11+
method: 'personal_sign', params: [account, 'The website wants permission to access and manage your data vault']
12+
}).then(sig => provider.request({
13+
// make sure the wallet signing is deterministic
14+
method: 'personal_sign', params: [account, 'The website wants permission to access and manage your data vault']
15+
}).then(sig2 => {
16+
if (sig2 !== sig) throw new Error('Sorry, your wallet does not support encryption. You cannot access your Data Vault')
17+
// Check the size of r and s - // 0x r(32) s(32) v(1)
18+
if (sig.length !== 132) throw new Error('Sorry, your wallet does not support encryption. You cannot access your Data Vault')
19+
const r = sig.slice(2, 66)
20+
const s = sig.slice(66, 130)
21+
22+
return HmacSHA512(r, s).toString()
23+
})
24+
).then(hmac => ({
25+
// split the resulting string in half, and use one half as
26+
// the encryption key and the other half as hmac secret
27+
key: hmac.substring(0, 64),
28+
macKey: hmac.substring(64, 128)
29+
}))
30+
31+
export const encrypt = (key, message, macKey) => {
32+
const iv = WordArray.random(16)
33+
34+
const c = AES.encrypt(message, key, { iv: iv, mode: ModeCTR }).toString()
35+
// do a MAC preventing padding oracle attacks
36+
const m = HmacSHA256(iv + c, macKey).toString()
37+
38+
return iv + c + m
39+
}
40+
41+
export const decrypt = (key, cipher, macKey) => {
42+
const iv = encHex.parse(cipher.substring(0, 32))
43+
const c = cipher.substring(32, cipher.length - 64)
44+
const m = cipher.substring(cipher.length - 64)
45+
46+
const calculatedM = HmacSHA256(iv + c, macKey).toString()
47+
48+
if (calculatedM !== m) {
49+
throw new Error('Decryption failed. Corrupted message.')
50+
}
51+
52+
return AES.decrypt(c, key, { iv: iv, mode: ModeCTR }).toString(encUtf8)
53+
}

modules/ipfs-cpinner-client/src/encryption-manager/index.ts renamed to modules/ipfs-cpinner-client/src/encryption-manager/asymmetric.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { encrypt as ethEncrypt } from 'eth-sig-util'
22
import { Web3Provider } from '../web3provider/types'
3-
import { DecryptFn, EncryptionManagerConfig, GetEncryptionPublicKeyFn } from './types'
3+
import { DecryptFn, EncryptionManagerConfig, GetEncryptionPublicKeyFn, IEncryptionManager } from './types'
44

5-
class EncryptionManager {
5+
class EncryptionManager implements IEncryptionManager {
66
getEncryptionPublicKey: GetEncryptionPublicKeyFn
77
private decryptInWallet: DecryptFn
88
constructor ({ getEncryptionPublicKey, decrypt }: EncryptionManagerConfig) {

modules/ipfs-cpinner-client/src/encryption-manager/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ export type EncryptionManagerConfig = {
55
decrypt?: DecryptFn
66
getEncryptionPublicKey?: GetEncryptionPublicKeyFn
77
}
8+
9+
export interface IEncryptionManager {
10+
encrypt(data: string): Promise<string>
11+
decrypt(data: string): Promise<string>
12+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { IEncryptionManager } from './types'
2+
import { encrypt, decrypt, generateKeyViaRPC } from './aes'
3+
import { Web3Provider } from '../web3provider/types'
4+
5+
class EncryptionManager implements IEncryptionManager {
6+
private key: string
7+
private macKey: string
8+
9+
constructor (key: string, macKey: string) {
10+
this.key = key
11+
this.macKey = macKey
12+
}
13+
14+
encrypt = (data: string) => Promise.resolve(encrypt(this.key, data, this.macKey))
15+
decrypt = (cipher: string) => Promise.resolve(decrypt(this.key, cipher, this.macKey))
16+
17+
static fromWeb3Provider = (provider: Web3Provider) =>
18+
provider.request({
19+
method: 'eth_accounts'
20+
}).then(accounts => generateKeyViaRPC(provider, accounts[0]))
21+
.then(({ key, macKey }) => new EncryptionManager(key, macKey))
22+
}
23+
24+
export default EncryptionManager

modules/ipfs-cpinner-client/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import axios from 'axios'
22
import AuthManager from './auth-manager'
3-
import EncryptionManager from './encryption-manager'
3+
import EncryptionManager from './encryption-manager/asymmetric'
44
import { AUTHENTICATION_ERROR, MAX_STORAGE_REACHED, SERVICE_MAX_STORAGE_REACHED, UNKNOWN_ERROR } from './constants'
55
import {
66
CreateContentPayload, CreateContentResponse,

modules/ipfs-cpinner-client/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import AuthManager from './auth-manager'
2-
import EncryptionManager from './encryption-manager'
2+
import EncryptionManager from './encryption-manager/asymmetric'
33
export type GetContentPayload = { key: string }
44
export type GetContentResponsePayload = { id: string, content: string }
55
export type CreateContentPayload = { key: string, content: string }

modules/ipfs-cpinner-client/test/create-content.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import MockDate from 'mockdate'
66
import localStorageMockFactory from './localStorageMockFactory'
77
import { deleteDatabase, resetDatabase, startService, testTimestamp, setupDataVaultClient, testMaxStorage, getEncryptionPublicKeyTestFn, decryptTestFn } from './util'
88
import { MAX_STORAGE_REACHED } from '../src/constants'
9-
import EncryptionManager from '../src/encryption-manager'
9+
import EncryptionManager from '../src/encryption-manager/asymmetric'
1010

1111
jest.setTimeout(12000)
1212

modules/ipfs-cpinner-client/test/custom-storage.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Server } from 'http'
44
import { customStorageFactory, decryptTestFn, deleteDatabase, getEncryptionPublicKeyTestFn, identityFactory, resetDatabase, startService, testTimestamp } from './util'
55
import MockDate from 'mockdate'
66
import AuthManager from '../src/auth-manager'
7-
import EncryptionManager from '../src/encryption-manager'
7+
import EncryptionManager from '../src/encryption-manager/asymmetric'
88

99
jest.setTimeout(12000)
1010

0 commit comments

Comments
 (0)