Last active
February 8, 2019 09:34
-
-
Save piroor/dee3b71bedddf22c4bc264eea6fd97ce to your computer and use it in GitHub Desktop.
Simple String Encryptor (example)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Simple String Encryptor with common key cryptosystem (example) | |
| Usage: | |
| // The first argument of the constructor is the algorithm. | |
| // If you don't specify any algorithm, AES-CTR 256bit is used. | |
| const encryptor = new Encryptor({ name: 'AES-CTR', length: 256 }); | |
| const counter = crypto.getRandomValues(new Uint8Array(16)); | |
| const encrypted = await encryptor.encryptString('Hello world!', { counter }); | |
| console.log(encrypted); // => BinaryString | |
| console.log(btoa(encrypted)); // => Base64 String, safe to save | |
| const decrypted = await encryptor.decryptString(encrypted, { counter }); | |
| console.log(decrypted); // => "Hello world!" | |
| // The key is exportable as a JSON Web Key. | |
| // You can store it to local storage or somewhere for furthur sessions. | |
| localStorage.setItem('key', await encryptor.exportKey()); | |
| localStorage.setItem('secret message', btoa(encrypted)); | |
| // encrypt/decrypt over sessions | |
| const key = localStorage.getItem('key'); | |
| const encryptedMessage = atob(localStorage.getItem('secret message')); | |
| const newEncryptor = new Encryptor({ name: 'AES-CTR', length: 256 }, key); | |
| console.log(await newEncryptor.decryptString(encryptedMessage)); // => "Hello world!" | |
| References: | |
| https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API | |
| https://github.com/diafygi/webcrypto-examples | |
| http://var.blog.jp/archives/62330155.html | |
| Creator: | |
| YUKI "Piro" Hiroshi <[email protected]> | |
| License: | |
| MIT | |
| */ | |
| class Encryptor { | |
| constructor(algorithm, JSONWebKey = null) { | |
| this.$algorithm = algorithm || { name: 'AES-CTR', length: 256 }; | |
| this.$initialized = this.init(JSONWebKey); | |
| } | |
| async init(JSONWebKey) { | |
| if (JSONWebKey) { | |
| try { | |
| this.key = await crypto.subtle.importKey( | |
| 'jwk', | |
| JSONWebKey, | |
| { name: this.$algorithm.name }, | |
| false, | |
| ['encrypt', 'decrypt'] | |
| ); | |
| } | |
| catch(e) { | |
| } | |
| } | |
| if (!this.key) { | |
| this.key = await this.generateKey(); | |
| } | |
| } | |
| async exportKey() { | |
| await this.$initialized; | |
| return crypto.subtle.exportKey('jwk', this.key); | |
| } | |
| async generateKey() { | |
| const algorithm = { | |
| name: this.$algorithm.name, | |
| length: this.$algorithm.length | |
| }; | |
| return crypto.subtle.generateKey( | |
| algorithm, | |
| true, | |
| ['encrypt', 'decrypt'] | |
| ); | |
| } | |
| async encrypt(input, options = null) { | |
| options = options || {}; | |
| await this.$initialized; | |
| const algorithm = { | |
| name: this.$algorithm.name | |
| }; | |
| switch (algorithm.name) { | |
| case 'AES-CBC': | |
| algorithm.iv = options.iv; | |
| break; | |
| case 'AES-CTR': | |
| algorithm.length = options.length || (options.counter ? 64 : 128); | |
| algorithm.counter = options.counter || new Uint8Array(16); | |
| break; | |
| case 'AES-GCM': | |
| algorithm.iv = options.iv; | |
| if (options.additionalData) | |
| algorithm.additionalData = options.additionalData; | |
| if (typeof options.additionalData == 'string') | |
| algorithm.additionalData = (new TextEncoder()).encode(algorithm.additionalData).buffer; | |
| algorithm.tagLength = options.tagLength || 128; | |
| break; | |
| } | |
| return crypto.subtle.encrypt( | |
| algorithm, | |
| this.key, | |
| input | |
| ); | |
| } | |
| async decrypt(encrypted, options = null) { | |
| options = options || {}; | |
| const algorithm = { | |
| name: this.$algorithm.name | |
| }; | |
| switch (algorithm.name) { | |
| case 'AES-CBC': | |
| algorithm.iv = options.iv; | |
| break; | |
| case 'AES-CTR': | |
| algorithm.length = options.length || (options.counter ? 64 : 128); | |
| algorithm.counter = options.counter || new Uint8Array(16); | |
| break; | |
| case 'AES-GCM': | |
| algorithm.iv = options.iv; | |
| if (options.additionalData) | |
| algorithm.additionalData = options.additionalData; | |
| if (typeof options.additionalData == 'string') | |
| algorithm.additionalData = (new TextEncoder()).encode(algorithm.additionalData).buffer; | |
| algorithm.tagLength = options.tagLength || 128; | |
| break; | |
| } | |
| await this.$initialized; | |
| return crypto.subtle.decrypt( | |
| algorithm, | |
| this.key, | |
| encrypted | |
| ); | |
| } | |
| async encryptString(input, options = null) { | |
| const data = (new TextEncoder()).encode(input); | |
| const encryptedData = await this.encrypt(data, options); | |
| return Array.from(new Uint8Array(encryptedData), char => String.fromCharCode(char)).join(''); | |
| } | |
| async decryptString(encrypted, options = null) { | |
| const data = Uint8Array.from(encrypted.split(''), char => char.charCodeAt(0)); | |
| const decryptedData = await this.decrypt(data, options); | |
| return (new TextDecoder()).decode(new Uint8Array(decryptedData)); | |
| } | |
| }; |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Benchmark