Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

comdb

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

comdb - npm Package Compare versions

Comparing version 2.0.0-beta to 3.0.0-beta

10

index.js

@@ -131,10 +131,10 @@ 'use strict'

assert(this._crypt, 'Must set a password with `.setPassword(password)` before encrypting documents.')
return this._crypt.encrypt(doc)
return this._crypt.encrypt(JSON.stringify(doc))
}
// decryption convenience function; takes the output of .encrypt
PouchDB.prototype.decrypt = function (payload) {
PouchDB.prototype.decrypt = async function (payload) {
assert(this._crypt, 'Must set a password with `.setPassword(password)` before decrypting documents.')
return this._crypt.decrypt(payload).then((plaintext) => {
return JSON.parse(plaintext)
})
const plaintext = await this._crypt.decrypt(payload)
const doc = JSON.parse(plaintext)
return doc
}

@@ -141,0 +141,0 @@ // destroy wrapper that destroys both the encrypted and decrypted DBs

'use strict'
const assert = require('assert')
const crypto = require('crypto')
const ALGORITHM_NAME = 'aes-256-gcm'
const ALGORITHM_NONCE_SIZE = 12
const ALGORITHM_TAG_SIZE = 16
const ALGORITHM_KEY_SIZE = 32
const PBKDF2_NAME = 'sha512'
const PBKDF2_SALT_SIZE = 16
const PBKDF2_ITERATIONS = 10000
const { secretbox, hash, randomBytes } = require('tweetnacl')
const { decodeUTF8, encodeUTF8, encodeBase64, decodeBase64 } = require('tweetnacl-util')
module.exports = class Crypt {
constructor (password, options = {}) {
constructor (password) {
assert(password, 'A password is required for encryption or decryption.')
this.password = password
this.algorithmName = options.algorithmName || ALGORITHM_NAME
this.algorithmNonceSize = options.algorithmNonceSize || ALGORITHM_NONCE_SIZE
this.algorithmTagSize = options.algorithmTagSize || ALGORITHM_TAG_SIZE
this.algorithmKeySize = options.algorithmKeySize || ALGORITHM_KEY_SIZE
this.pbkdf2Name = options.pbkdf2Name || PBKDF2_NAME
this.pbkdf2SaltSize = options.pbkdf2SaltSize || PBKDF2_SALT_SIZE
this.pbkdf2Iterations = options.pbkdf2Iterations || PBKDF2_ITERATIONS
this._key = hash(decodeUTF8(this.password)).slice(0, secretbox.keyLength)
}
encrypt (plaintext) {
if (typeof plaintext !== 'string') return this.encrypt(JSON.stringify(plaintext))
const salt = crypto.randomBytes(this.pbkdf2SaltSize)
return this._pbkdf2(salt).then((key) => {
const ciphertextAndNonceAndSalt = Buffer.concat([
salt,
this._encryptWithKey(Buffer.from(plaintext, 'utf8'), key)
])
return ciphertextAndNonceAndSalt.toString('base64')
})
async encrypt (plaintext) {
const nonce = randomBytes(secretbox.nonceLength)
const messageUint8 = decodeUTF8(plaintext)
const box = secretbox(messageUint8, nonce, this._key)
const fullMessage = new Uint8Array(nonce.length + box.length)
fullMessage.set(nonce)
fullMessage.set(box, nonce.length)
const base64FullMessage = encodeBase64(fullMessage)
return base64FullMessage
}
decrypt (base64CiphertextAndNonceAndSalt) {
const ciphertextAndNonceAndSalt = Buffer.from(base64CiphertextAndNonceAndSalt, 'base64')
const salt = ciphertextAndNonceAndSalt.slice(0, this.pbkdf2SaltSize)
const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(this.pbkdf2SaltSize)
return this._pbkdf2(salt).then((key) => {
return this._decryptWithKey(ciphertextAndNonce, key).toString('utf8')
})
async decrypt (messageWithNonce) {
const messageWithNonceAsUint8Array = decodeBase64(messageWithNonce)
const nonce = messageWithNonceAsUint8Array.slice(0, secretbox.nonceLength)
const message = messageWithNonceAsUint8Array.slice(
secretbox.nonceLength,
messageWithNonce.length
)
const decrypted = secretbox.open(message, nonce, this._key)
if (!decrypted) {
throw new Error('Could not decrypt!')
} else {
return encodeUTF8(decrypted)
}
}
_pbkdf2 (salt) {
return new Promise((resolve, reject) => {
crypto.pbkdf2(
Buffer.from(this.password, 'utf8'),
salt,
this.pbkdf2Iterations,
this.algorithmKeySize,
this.pbkdf2Name,
(err, key) => {
if (err) return reject(err)
return resolve(key)
})
})
}
_encryptWithKey (plaintext, key) {
const nonce = crypto.randomBytes(this.algorithmNonceSize)
const cipher = crypto.createCipheriv(this.algorithmName, key, nonce)
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()])
return Buffer.concat([nonce, ciphertext, cipher.getAuthTag()])
}
_decryptWithKey (ciphertextAndNonce, key) {
const nonce = ciphertextAndNonce.slice(0, this.algorithmNonceSize)
const ciphertext = ciphertextAndNonce.slice(
this.algorithmNonceSize,
ciphertextAndNonce.length - this.algorithmTagSize)
const tag = ciphertextAndNonce.slice(ciphertext.length + this.algorithmNonceSize)
const cipher = crypto.createDecipheriv(this.algorithmName, key, nonce)
cipher.setAuthTag(tag)
return Buffer.concat([cipher.update(ciphertext), cipher.final()])
}
}
{
"name": "comdb",
"version": "2.0.0-beta",
"version": "3.0.0-beta",
"description": "A PouchDB plugin that transparently encrypts and decrypts its data.",

@@ -28,3 +28,5 @@ "main": "index.js",

"lodash.isequal": "^4.5.0",
"pouchdb": "^7.2.2"
"pouchdb": "^7.2.2",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1"
},

@@ -31,0 +33,0 @@ "devDependencies": {

@@ -9,3 +9,3 @@ # ComDB

A [PouchDB](https://pouchdb.com/) plugin that transparently encrypts and decrypts its data so that only encrypted data is sent during replication, while encrypted data that you receive is automatically decrypted.
A [PouchDB](https://pouchdb.com/) plugin that transparently encrypts and decrypts its data so that only encrypted data is sent during replication, while encrypted data that you receive is automatically decrypted. Uses [TweetNaCl](https://www.npmjs.com/package/tweetnacl) for cryptography.

@@ -138,10 +138,2 @@ As an example, here's what happens when you replicate data to a [CouchDB](https://couchdb.apache.org/) cluster:

- `opts.opts`: An options object passed to the encrypted database's constructor. Use this to pass any options accepted by [PouchDB's constructor](https://pouchdb.com/api.html#create_database).
- `opts.crypt`: Options for ComDB's crypto tooling.
- `opts.crypt.algorithmName`: Name of the encryption algorithm.
- `opts.crypt.algorithmNonceSize`: Size of generated nonces.
- `opts.crypt.algorithmTagSize`: Size of the auth tags used.
- `opts.crypt.algorithmKeySize`: Size of generated keys.
- `opts.crypt.pbkdf2Name`: Name of the hashing algorithm.
- `opts.crypt.pbkdf2SaltSize`: Size of generates salts.
- `opts.crypt.pbkdf2Iterations`: Number of iterations to hash data.

@@ -148,0 +140,0 @@ ### `db.bulkDocs(docs, [opts], [callback])`

@@ -7,13 +7,37 @@ /* global describe, it */

const PLAINTEXT = 'hello world'
const PASSWORD = 'password'
const TEST_LENGTH = 1e4 // note: 1e4 = 1 and 4 zeroes (10,000)
describe('crypt', function () {
it('should do the crypto dance', function () {
const plaintext = 'hello world'
const password = 'password'
const crypt = new Crypt(password)
return crypt.encrypt(plaintext).then((ciphertext) => {
return crypt.decrypt(ciphertext)
}).then((newtext) => {
assert.strictEqual(newtext, plaintext)
})
it('should do the crypto dance', async function () {
const crypt = new Crypt(PASSWORD)
const ciphertext = await crypt.encrypt(PLAINTEXT)
const decryptext = await crypt.decrypt(ciphertext)
assert.strictEqual(decryptext, PLAINTEXT)
})
it('should fail to decrypt ok', async function () {
const crypt = new Crypt(PASSWORD)
const crypt2 = new Crypt(PASSWORD + 'a')
const ciphertext = await crypt.encrypt(PLAINTEXT)
let failed = false
try {
await crypt2.decrypt(ciphertext)
} catch (e) {
assert.equal(e.message, 'Could not decrypt!')
failed = true
}
assert(failed)
})
it(`should do the crypto dance ${TEST_LENGTH} times`, async function () {
this.timeout(TEST_LENGTH) // assume each op will take no more than 1ms
const crypt = new Crypt(PASSWORD)
for (let i = 0; i < TEST_LENGTH; i++) {
const ciphertext = await crypt.encrypt(PLAINTEXT)
const decryptext = await crypt.decrypt(ciphertext)
assert.strictEqual(decryptext, PLAINTEXT)
}
})
})

@@ -75,7 +75,7 @@ /* global describe, it, before, after */

// 1. write to encrypted db
const payload = await this.crypt.encrypt({
const payload = await this.crypt.encrypt(JSON.stringify({
_id: 'hello',
_rev: '1-15f65339921e497348be384867bb940f',
hello: 'world'
})
}))
await this.dbs.encrypted.post({ payload })

@@ -82,0 +82,0 @@ // 2. hook up decrypted db to encrypted

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc