shrine512
Introduction
Convenient and beginner-friendly module for encryption. It is very very easy to use and perform 'AES-256-GCM' (SHA-512) encryption/decryption of strings, objects, filecontent, etc. It is using Node.js' native crypto API, which is very good, but not very self-explanatory or at all easy to use unless you are quite familiar with the many concepts of cryptography prior to using it. If you just want to get started with encrypting something, this module makes it as accessible as is possible.
Fun fact: The encryption algorithm itself was originally developed by the NSA of the US government.
For those who aren't sure when less could be enough, this module offers a very heavy type of encryption. However, this heaviness is also obvious from a computational point of view, meaning that it takes a lot longer to encrypt and decrypt data with this module compared to other methods whenever a little or even sometimes much less computational effort would otherwise suffice - that is, in terms of protecting against brute-force attacks to a degree where that kind of attack is no longer practically feasable. To summarize: it's easy. It's always a secure type of encryption, but often way more than neccessary - and therefore slow.
Disclaimer! The success of brute-force attacks are a matter of chance by nature, so even though it is neither practically relevant to worry about when sufficiently encrypted, nor realistic to randomly guess a strong encryption key, it is, however theoretically possible to correctly guess any secret, incl. any encryption key.
Benjamin M�ller Jensen
Why make this?
-
Many encryption libraries / tools require a lot of prior knowledge to know how to even use them. This does not. It's so simple, it's basically just an on/off switch. I might just be half-blind or unlucky, but I have not come across any module that is just absolutely simple with good defaults or fixed settings out of the box, so I thought to fill the gap as to help javascript on its never-ending way to becoming ever more friendly to newcomers than it already is.
-
Although other means of encryption can be 'good enough' in various specific use-cases, this tool consistently offers somewhere between 'way too much' and 'definately enough' encryption, assuming your encryption key is not (figuratively and not isolated to) just "1234".
Features
-
A single exposed class with only two simple instance methods, encrypt & decrypt.
-
Support for both strings & objects. So anything JSON.stringify-parsable, basically. In most cases, that means anything from file-contents to whatever.
-
Tested with Mark Sugarburger's testing framework, Jest indeed with 100 % code-coverage.
-
A 128-bit (maximum) auth-tag length is always used and is automatic and not configurable. See this if you should be confused as to why tag-length is important.
-
Serializes with JSON.stringify and unserializes with JSON.parse. If that should not suffice, consider handling serialization yourself prior to passing your data to the encryption method. More universal serialization is a todo :)
-
Encryption is undeterministic, meaning that the same data encrypted multiple times does not produce identical output, so that it's not just obvious and self-revealing the fact that it was so.
-
Salt (IV) is automatic and also very random. This is to avoid being open to plaintext-checking attacks. The basics of this form of attack being that an attacker can predict the IV if the same one is used repeatedly. In certain scenarios, an attacker can even use the encryption operation as an 'oracle' and can thereby deduce the value of low entropy plaintext-blocks if encrypted by the same encryption key (of which the IV is part, along with the encryption key). So it's a way to indistinguisly imitate yourself creating a new unique key every time you encrypt something. This happens natively in t
-
This and the encryption key are both stored so that it is inaccesible at runtime to all except the instance methods themselves, and can thereby be used multiple times without having to pass the encryption key around in an insecure way, potentially exposing it by accident. When the instance is garbage-collected by the runtime environment (V8, for instance), the encryption key is also destroyed simultaneously and to date inevitably, and to date with no possible means of recovery.
-
The used algorithm natively attempts to compensate for short encryption keys, but please create long encryption keys instead of relying on that.
Table of Contents
- shrine512
- Features
- Table of Contents
- Installation - Compatibility - Dependencies - Package
- Usage - Import - Require - Example
- Tests - Coverage
- Source Code
- main.js - Module entry point
Installation
Compatibility
Dependencies
Package
npm install --save shrine512
Usage
Import
Require
const Shrine512 =
Example
async { try //for simpler syntax const str = JSONstringify const log = consolelog //pass secret to new instance const shrine = 'sum sickret, huh' //encrypt const enStr = await shrine const enObj = await shrine //decrypt const deStr = await shrine const deObj = await shrine //assert //console catch e } /** * Console output: * ------------------ * { * enStr: '6abf361826ff8268a0dc60f77b9a8868145441ee921077c00b4 (...)', * enObj: 'f8ab65f0bac60aa070c3649dca602c818fe364a39d0219d8ceb (...)', * deStr: 'a string', * deObj: { an: 'object' } * } */
Tests
npm run test
Coverage
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---|---|---|---|---|---|
All files | 100 | 100 | 100 | 100 | |
Shrine512.js | 100 | 100 | 100 | 100 | |
createDecryptor.js | 100 | 100 | 100 | 100 | |
createEncryptor.js | 100 | 100 | 100 | 100 | |
createKeyRetreiver.js | 100 | 100 | 100 | 100 | |
isNonEmpStr.js | 100 | 100 | 100 | 100 | |
isObject.js | 100 | 100 | 100 | 100 | |
isUndefined.js | 100 | 100 | 100 | 100 | |
main.js | 0 | 0 | 0 | 0 |
PASS tests/shrine-512t.est.js (6.195s)
- Test Suites: 1 passed, 1 total
- Tests: 27 passed, 27 total
- Snapshots: 0 total
- Time: 7.155s
- Ran all test suites.
Shrine512 works fine
-
? works with promises (129ms)
-
? works with random strings of random lengths between 1 and 40 with random encryption keys of random lengths between 1 and 16 (1 (1997ms)
-
? works with objects containing a string of random length between 1 and 40 with random encryption keys of random lengths betweeneen 1 and 16 (1993ms)
-
? works with async/await (124ms)
-
? works with async/await and resolves (125ms)
-
? throws if undefined to encrypt is provided explicit
-
? throws if undefined to encrypt is provided (1ms)
-
? throws if undefined to encrypt is provided
-
? throws if null to encrypt is provided (63ms)
-
? throws if undefined to decrypt is provided
-
? throws if null to decrypt is provided
-
? does not throw if something to encrypt is provided (62ms)
-
? does not throw if something to decrypt is provided (62ms)
-
? decrypts with a different shrine object than it was encrypted with, provided identical secret (126ms)
-
? throws and does not decrypt with a different secret than was used to encrypt (63ms)
-
? throws on zero-length secret (3ms)
-
? throws on null secret
-
? throws on undefined secret (1ms)
-
? throws on non- string secret
-
? throws on zero-length input to encrypt (63ms)
-
? throws on null input to encrypt
-
? throws on undefined input to encrypt
-
? throws on zero-length input to decrypt
-
? throws on undefined input to decrypt
-
? throws on zero-length string input to decrypt (1ms)
-
? throws on non- string input to encrypt (63ms)
-
? throws on non- string input to encrypt (62ms)
Trivial, but Important Disclaimers and Warnngs About Encryption Security
- In too many cases, the cause of data loss is forgetting one's own password/key.
- In too many cases, the cause of privacy loss (leaked data or unintended attacker access) is storing one's password/key somewhere that is not secure.
- Please do not forget the fact that anyone with the correct encryption key can access any encrypted data as easily as its owner.
- Please do remember that short key are far less secure. 16 characters is recommeneded.
- It's not important to create an unreadable key, such as this: "js9JkwusS73KSks.__sa7S". It's important that it's 16 characters LONG... AND non-trivial. The point is to make it unguessable by automatic/programmatical means, even if any given person/software has detailed information about you. Hint: It's impossible for anyone/thing to ever obtain certain information, such as someone's daughter's birtdate, so it's best to always use that whenever you can (sarcasm).
- Do always stay on top of present time threats that may exist that could comprimise or expoose your encryption key. For instance, watch out for keyloggers. But would you just get a few things in order, and it should, however, be an extremely secure form of achieving data-privacy.
Support
Please open an issue for support.
Contributing
Please contribute using Github Flow. Create a branch, add commits, and open a pull request.
BSD-3 License
� Copyright 2019 Benjamin M�ller Jensen <bemoje@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Source Code
- version: 1.0.0
- author: Benjamin Moeller Jensen <bemoje@gmail.com>
- license: BSD-3 Clause
Table of Contents
createDecryptor
Returns the function that performs decryption.
Parameters
sigrid
string The secret phrasesym
object The object used as key in the weakmap. @see createKeyRetreiver
Returns object
createEncryptor
Returns the function that performs the encryption.
Parameters
sigrid
string The secret phrasesym
object The object used as key in the weakmap. @see createKeyRetreiver
Returns object
isNonEmpStr
Returns true of val is a string that isnt empty
Parameters
val
any
Returns boolean
isObject
Returns true if val is of type object
Parameters
val
any
Returns boolean
isUndefined
Returns true if val is undefined or null
Parameters
val
any
Returns boolean
Shrine512
The only public export. The constructor takes a secret encryption key and constructs an object from it that has two methods on it that can encrypt and decrypt respective, both strings and objects.
main.js
Module entry point
/** * Convenient and very VERY very easy to use 'AES-256-GCM' (SHA-512) encryption/decryption of strings, objects, filecontent, etc. * @module Shrine512 * @version 1.0.0 * @author Benjamin Moeller Jensen <bemoje@gmail.com> * @license BSD-3 Clause */
Shrine512.js
/** * The only public export. The constructor takes a secret encryption key and constructs an object from it that has two methods on it that can encrypt and decrypt respective, both strings and objects. * @public * @class Shrine512 */ /** * @constructor * @param */ { if ! throw 'Expected non-empty string value.' const sym = dam: this Symbol', man' thisencrypt = thisdecrypt = Object }
isUndefined.js
/** * Returns true if val is undefined or null * @module isUndefined * @param * @returns */ { return val === undefined || val === null}
isObject.js
/** * Returns true if val is of type object * @module isObject * @param * @returns */ { return typeof val === 'object'}
isNonEmpStr.js
/** * Returns true of val is a string that isnt empty * @module isNonEmpStr * @param * @returns */ { return typeof val === 'string' && vallength > 0}
createKeyRetreiver.js
const map = /** * Creates a function that creates the correct key provided a encryption key and it is also passed a unique symbol along with 'this'-object reference created inside the constructor of the exported class. So only that object will be able to retrieve this key-getter from the weakmap. * If the object has been destroyed, a new key retriever can be created by creating a new object witht the class constructor, and it will not be the same key-getter function exactly, but it will decrypt just as well. * @private * @module createKeyRetreiver * @param * @param */ { const getter = crypto map return map}
createEncryptor.js
const algorithm = 'aes-256-gcm'const ivLength = 16const saltLength = 64 /** * Returns the function that performs the encryption. * @module createEncryptor * @param * @param * @returns */ { const keyGetter = return { try if throw 'Expected non-empty string value or object.' let val let isObj = if !isObj if ! val = value isObj = true else val = value else val = value const iv = crypto const salt = crypto const key = const cipher = crypto val = isObj ? JSON : value const encrypted = Buffer const tag = cipher const ret = Buffer return ret catch e return e }}
createDecryptor.js
const tagLength = 16const algorithm = 'aes-256-gcm'const ivLength = 16const saltLength = 64const tagPosition = saltLength + ivLengthconst encryptedPosition = tagPosition + tagLength /** * Returns the function that performs decryption. * @module createDecryptor * @param * @param * @returns */ { const keyGetter = return { if ! throw 'Expected non-empty string value.' try const stringValue = Buffer const salt = stringValue const iv = stringValue const tag = stringValue const encrypted = stringValue const key = const decipher = crypto decipher let ret = decipher + decipher try return JSON catch e return ret catch e return e }}
shrine512.test.js
'use strict'const make = { let rndStrings = for let i = 1; i <= 4; i++ rndStrings return rndStrings } { let rndObjects = for let i = 1; i <= 4; i++ rndObjects return rndObjects } { let rndSecrets = for let i = 1; i <= 4; i++ rndSecrets let shrines = for let secret of rndSecrets shrines return shrines }