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

@tabcat/encrypted-docstore

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tabcat/encrypted-docstore - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

2

package.json
{
"name": "@tabcat/encrypted-docstore",
"version": "0.0.1",
"version": "0.0.2",
"description": "mount encrypted docstores with a key",

@@ -5,0 +5,0 @@ "main": "src/encryptedDocstore.js",

@@ -6,2 +6,3 @@ # encrypted-docstore

## Usage

@@ -36,3 +37,3 @@ install with npm:

// get,put, del, query all exposed on encDocstore and return results VERY similar orbitDocstore
// get,put, del, query all exposed on encDocstore and returned results should be identical to docstore methods

@@ -44,3 +45,3 @@ ```

### Static Properties:
### Static Methods:
#### EncDoc.mount(docstore, key)

@@ -100,24 +101,34 @@ >mount an encrypted docstore

### Instance Propterties:
#### encDoc.get(_id)
#### encDoc.encrypted
> the orbit docstore being used as the encrypted docstore
#### encDoc.key
> an instance of the Key class from src/key.js
### Instance Methods:
> get, put, del, query all work by encapsulating the field it is indexed by (default is \_id) and should behave the same
#### encDoc.get(key)
see: https://github.com/orbitdb/orbit-db/blob/master/API.md#getkey-1
differences:
- returns doc with EncDoc doc properties ciphertext and iv, do not use these property names for your docs they will be overwritten
no visible differences
#### encDoc.put(doc)
>see: https://github.com/orbitdb/orbit-db/blob/master/API.md#putdoc
differences:
- returns doc with EncDoc doc properties ciphertext and iv, do not use these property names for your docs they will be overwritten
- doc is taken, properties than are not _id, ciphertext or iv are grabbed and encrypted to make the new ciphertext and iv
#### encDoc.del(_id)
no visible differences
#### encDoc.del(key)
>see: https://github.com/orbitdb/orbit-db/blob/master/API.md#delkey-1
no differences
no visible differences
#### encDoc.query(mapper)
>see: https://github.com/orbitdb/orbit-db/blob/master/API.md#querymapper
no visisble differences:
- mapper is fed unencrypted docs like those returned from the get method
differences:
- when calling with option fullOp:
+ the payload.value is the decrypted/decapsulated doc.
+ the payload.key which would usually match the payload.value[indexBy] field (indexBy default is '\_id')
does not.
+ anything in the fullOp entry relating to hashing the real payload.value will not match the payload.value
- when not calling with option fullOp:
+ no visible differences

@@ -42,3 +42,3 @@

Buffer.from(
(await key.encrypt(decodedRoot, decodedRoot)).bytes
(await key.encrypt(decodedRoot, decodedRoot)).cipherbytes
)

@@ -104,11 +104,40 @@ )

// helpers
async decryptRecords(encryptedRecords) {
return await Promise.all(
encryptedRecords.map(encDoc => this.key.decryptMsg(encDoc))
)
}
// docstore operations
async get(_id) {
if (_id === undefined) {
throw new Error('_id is undefined')
async get(indexKey, caseSensitive = false) {
if (indexKey === undefined) {
throw new Error('indexKey is undefined')
}
return await Promise.all(
this.encrypted.get(_id).map((doc) => this._id.decryptMsg(doc))
)
const indexBy = this.encrypted.options.indexBy
// code taken from orbit document store get method:
// https://github.com/orbitdb/orbit-db-docstore/blob/master/src/DocumentStore.js
indexKey = indexKey.toString()
const terms = indexKey.split(' ')
indexKey = terms.length > 1
? replaceAll(indexKey, '.', ' ').toLowerCase()
: indexKey.toLowerCase()
const replaceAll = (str, search, replacement) =>
str.toString().split(search).join(replacement)
const search = (e) => {
if (terms.length > 1) {
return replaceAll(e, '.', ' ').toLowerCase().indexOf(indexKey) !== -1
}
return e.toLowerCase().indexOf(indexKey) !== -1
}
const filter = caseSensitive
? (i) => i[indexBy].indexOf(indexKey) !== -1
: (i) => search(i[indexBy])
const records = await this.decryptRecords(this.encrypted.query(() => true))
return await Promise.all(records.map(res => res.internal).filter(filter))
}

@@ -120,14 +149,40 @@

}
if (!doc._id) {
throw new Error('doc requires an _id field')
const indexBy = this.encrypted.options.indexBy
if (!doc[this.encrypted.options.indexBy]) {
throw new Error(`doc requires an ${indexBy} field`)
}
// since real _id is encapsulated in cipherbytes field and external _id is
// random, we must delete the old entry by querying for the same id
try { await this.del(doc[indexBy]) } catch(e) {}
return await this.encrypted.put(await this.key.encryptMsg(doc))
}
async del(key) {
return await this.encrypted.del(key)
async del(indexKey) {
if (indexKey === undefined) {
throw new Error('indexKey must be defined')
}
const indexBy = this.encrypted.options.indexBy
const records = await this.decryptRecords(this.encrypted.query(() => true))
const matches = records.filter(res => res.internal[indexBy] === indexKey)
// if a deletion fails this will clean it up old records
if (matches.length > 1) {
console.error(`there was more than one entry with internal key ${indexKey}`)
}
if (matches.length === 0) {
throw new Error(`No entry with key '${indexKey}' in the database`)
}
// only return first deletion to keep same api as docstore
return Promise.all(
matches.map(res => this.encrypted.del(res.external[indexBy]))
).then(arr => arr[0])
}
async query(mapper) {
async query(mapper, options = {}) {
if (mapper === undefined) {

@@ -137,10 +192,22 @@ throw new Error('mapper was undefined')

// decrypts each doc before mapper recieves them
const encMapper = async (encDoc) => mapper(await this.key.decryptMsg(encDoc))
// calls the query with the higher order decrypting mapper
const encQuery = this.encrypted.query(encMapper)
const fullOp = options.fullOp || false
const decryptFullOp = async(entry) => ({
...entry,
payload: {
...entry.payload,
value:await this.key.decryptMsg(entry.payload.value).then(res => res.internal),
},
})
return await Promise.all(
encQuery.map((encDoc) => this.key.decryptMsg(encDoc))
)
const index = this.encrypted._index
const indexGet = fullOp
? async(_id) => decryptFullOp(index._index[_id])
: async(_id) => index._index[_id]
? await this.key.decryptMsg(index._index[_id].payload.value)
.then(res => res.internal)
: null
const indexKeys = Object.keys(index._index)
return Promise.all(indexKeys.map(key => indexGet(key)))
.then(arr => arr.filter(mapper))
}

@@ -147,0 +214,0 @@

@@ -16,3 +16,3 @@

const randomBytes = async (bytes) => await crypto.getRandomValues(new Uint8Array(bytes))
const randomBytes = async (bytesLength) => await crypto.getRandomValues(new Uint8Array(bytesLength))

@@ -98,7 +98,7 @@ const encDocFields = ['_id', 'ciphertext', 'iv']

const _bytes = await crypto.subtle.encrypt(algo, this.key, bytes)
const ciphertext = ab2str(_bytes)
const cipherbytes = new Uint8Array(
await crypto.subtle.encrypt(algo, this.key, bytes)
)
return { ciphertext, bytes:_bytes, iv }
return { cipherbytes, iv }
}

@@ -130,7 +130,6 @@ async decrypt(bytes, iv) {

// makes new obj, stringifies and encodes only the encrypted fields
const bytes = str2ab(JSON.stringify(cipherSect(doc)))
const { ciphertext, iv } = await this.encrypt(bytes)
const bytes = str2ab(JSON.stringify(doc))
const { cipherbytes, iv } = await this.encrypt(bytes)
return { _id:doc._id, ciphertext, iv }
return { _id:`entry-${iv.join('')}`, cipherbytes, iv }
}

@@ -142,12 +141,8 @@ async function decryptDoc(encDoc) {

const bytes = str2ab(encDoc.ciphertext)
const decrypted = await this.decrypt(bytes, encDoc.iv)
// all fields within ciphertext doc field
const decrypted = await this.decrypt(encDoc.cipherbytes, encDoc.iv)
const clearObj = JSON.parse(ab2str(decrypted))
// clearObj spread first because maybe someone adds iv and ciphertext fields
// inside the ciphertext area for some weird reason and breaks something
return { ...clearObj, ...encDoc }
return { internal:clearObj, external:encDoc }
}
module.exports = Key
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