crypto-aes-gcm
Advanced tools
Comparing version 1.1.0 to 2.0.1
116
index.js
/** | ||
* @param {Window.Crypto} crypto | ||
* @param {string} plaintext | ||
* @param {string} password | ||
* @returns {Promise<string>} | ||
* Encrypts plaintext using AES-GCM with supplied password, for decryption with aes_gcm_decrypt() | ||
* | ||
* @param {string} plaintext plain text to be encrypted | ||
* @param {string} password password to use to encrypt plaintext | ||
* @returns {Promise<string>} encrypted cipher text | ||
*/ | ||
export async function encrypt(crypto, plaintext, password) { | ||
// encode password as UTF-8 | ||
const pwUtf8 = new TextEncoder().encode(password); | ||
// hash the password | ||
const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8); | ||
// get 96-bit random iv | ||
const iv = crypto.getRandomValues(new Uint8Array(12)); | ||
// iv as utf-8 string | ||
const ivStr = Array.from(iv).map(b => String.fromCharCode(b)).join(''); | ||
// specify algorithm to use | ||
const alg = { name: 'AES-GCM', iv: iv }; | ||
// generate key from pw | ||
const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['encrypt']); | ||
// encode plaintext as UTF-8 | ||
const ptUint8 = new TextEncoder().encode(plaintext); | ||
// encrypt plaintext using key | ||
const ctBuffer = await crypto.subtle.encrypt(alg, key, ptUint8); | ||
// ciphertext as byte array | ||
const ctArray = Array.from(new Uint8Array(ctBuffer)); | ||
// ciphertext as string | ||
const ctStr = ctArray.map(byte => String.fromCharCode(byte)).join(''); | ||
// iv+ciphertext base64-encoded | ||
return btoa(ivStr + ctStr); | ||
export async function aes_gcm_encrypt(plaintext, password) { | ||
// encode password as UTF-8 | ||
const pwUtf8 = new TextEncoder().encode(password); | ||
// hash the password | ||
const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8); | ||
// get 96-bit random iv | ||
const iv = crypto.getRandomValues(new Uint8Array(12)); | ||
// iv as utf-8 string | ||
const ivStr = Array.from(iv).map(b => String.fromCharCode(b)).join(''); | ||
// specify algorithm to use | ||
const alg = { name: 'AES-GCM', iv: iv }; | ||
// generate key from pw | ||
const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['encrypt']); | ||
// encode plaintext as UTF-8 | ||
const ptUint8 = new TextEncoder().encode(plaintext); | ||
// encrypt plaintext using key | ||
const ctBuffer = await crypto.subtle.encrypt(alg, key, ptUint8); | ||
// ciphertext as byte array | ||
const ctArray = Array.from(new Uint8Array(ctBuffer)); | ||
// ciphertext as string | ||
const ctStr = ctArray.map(byte => String.fromCharCode(byte)).join(''); | ||
// iv+ciphertext base64-encoded | ||
return btoa(ivStr + ctStr); | ||
} | ||
/** | ||
* @param {Window.Crypto} crypto | ||
* @param {string} ciphertext | ||
* @param {string} password | ||
* @returns {Promise<string>} | ||
* Decrypts ciphertext encrypted with aes_gcm_encrypt() using supplied password | ||
* | ||
* @param {string} ciphertext ciphertext to be decrypted | ||
* @param {string} password password to use to decrypt ciphertext | ||
* @returns {Promise<string>} decrypted plaintext | ||
*/ | ||
export async function decrypt(crypto, ciphertext, password) { | ||
// encode password as UTF-8 | ||
const pwUtf8 = new TextEncoder().encode(password); | ||
// hash the password | ||
const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8); | ||
// decode base64 iv | ||
const ivStr = atob(ciphertext).slice(0, 12); | ||
// iv as Uint8Array | ||
const iv = new Uint8Array(Array.from(ivStr).map(ch => ch.charCodeAt(0))); | ||
// specify algorithm to use | ||
const alg = { name: 'AES-GCM', iv: iv }; | ||
// generate key from pw | ||
const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['decrypt']); | ||
// decode base64 ciphertext | ||
const ctStr = atob(ciphertext).slice(12); | ||
// ciphertext as Uint8Array | ||
const ctUint8 = new Uint8Array(Array.from(ctStr).map(ch => ch.charCodeAt(0))); | ||
// note: why doesn't ctUint8 = new TextEncoder().encode(ctStr) work? | ||
export async function aes_gcm_decrypt(ciphertext, password) { | ||
// encode password as UTF-8 | ||
const pwUtf8 = new TextEncoder().encode(password); | ||
// hash the password | ||
const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8); | ||
// decode base64 iv | ||
const ivStr = atob(ciphertext).slice(0, 12); | ||
// iv as Uint8Array | ||
const iv = new Uint8Array(Array.from(ivStr).map(ch => ch.charCodeAt(0))); | ||
// specify algorithm to use | ||
const alg = { name: 'AES-GCM', iv: iv }; | ||
// generate key from pw | ||
const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['decrypt']); | ||
// decode base64 ciphertext | ||
const ctStr = atob(ciphertext).slice(12); | ||
// ciphertext as Uint8Array | ||
const ctUint8 = new Uint8Array(Array.from(ctStr).map(ch => ch.charCodeAt(0))); | ||
// note: why doesn't ctUint8 = new TextEncoder().encode(ctStr) work? | ||
try { | ||
// decrypt ciphertext using key | ||
const plainBuffer = await crypto.subtle.decrypt(alg, key, ctUint8); | ||
// return plaintext from ArrayBuffer | ||
return new TextDecoder().decode(plainBuffer); | ||
} catch (error) { | ||
throw new Error('decrypt failed'); | ||
} | ||
try { | ||
// decrypt ciphertext using key | ||
const plainBuffer = await crypto.subtle.decrypt(alg, key, ctUint8); | ||
// return plaintext from ArrayBuffer | ||
return new TextDecoder().decode(plainBuffer); | ||
} catch (error) { | ||
throw new Error('decrypt failed'); | ||
} | ||
} |
{ | ||
"name": "crypto-aes-gcm", | ||
"version": "1.1.0", | ||
"version": "2.0.1", | ||
"description": "Uses the SubtleCrypto interface of the Web Cryptography API to encrypt and decrypt text using AES-GCM (AES Galois counter mode)", | ||
"type": "module", | ||
"exports": { | ||
"node": "./node.js", | ||
"default": "./browser.js" | ||
}, | ||
"exports": "./index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "node tests/node.js" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/korywka/crypto-aes-gcm" | ||
}, | ||
"author": "Chris Veness (https://github.com/chrisveness)", | ||
"files": ["index.js"], | ||
"repository": "korywka/crypto-aes-gcm", | ||
"license": "MIT" | ||
} |
@@ -5,25 +5,25 @@ # crypto-aes-gcm | ||
This module uses the native (web)crypto API in node.js and the browser. | ||
This module uses the native WebCrypto API in [node.js](https://nodejs.org/api/webcrypto.html), Deno and the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). | ||
``` | ||
npm install crypto-aes-gcm | ||
``` | ||
Node and browser: [crypto-aes-gcm](https://www.npmjs.com/package/crypto-aes-gcm) | ||
Deno package: [https://deno.land/x/crypto_aes_gcm](https://deno.land/x/crypto_aes_gcm) | ||
```js | ||
import { aesGcmEncrypt, aesGcmDecrypt } from 'crypto-aes-gcm'; | ||
import { aes_gcm_encrypt, aes_gcm_decrypt } from '../index.js'; | ||
const password = '123456'; | ||
const message = 'i will never let you go'; | ||
// encryption | ||
const ciphertext = await aesGcmEncrypt('my secret text', 'pw'); | ||
aesGcmEncrypt('my secret text', 'pw').then(function(ciphertext) { console.log(ciphertext); }); | ||
const encrypted = await aes_gcm_encrypt(message, password); | ||
console.log(encrypted); | ||
// decryption | ||
const plaintext = await aesGcmDecrypt(ciphertext, 'pw'); | ||
aesGcmDecrypt(ciphertext, 'pw').then(function(plaintext) { console.log(plaintext); }); | ||
const decrypted = await aes_gcm_decrypt(encrypted, password); | ||
console.log(decrypted); | ||
console.log(message === decrypted); | ||
``` | ||
## Origin story | ||
## Original implementation | ||
The code was originally written by [Chris Veness](https://github.com/chrisveness) at [this gist](https://gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a). | ||
The code was originally written by [Chris Veness](https://gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a). | ||
Then, it was published as a package with node.js compatability out of the box. | ||
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
1
4042
3
65
2
1
1