Last active
August 20, 2025 15:02
-
-
Save tscholl2/dc7dc15dc132ea70a98e8542fefffa28 to your computer and use it in GitHub Desktop.
Revisions
-
tscholl2 revised this gist
Mar 22, 2018 . 2 changed files with 0 additions and 7 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,9 +1,3 @@ package main import ( 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 charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ import hashlib import os from binascii import hexlify, unhexlify -
Travis Scholl revised this gist
Sep 7, 2017 . 3 changed files with 9 additions and 39 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -56,5 +56,5 @@ func main() { c := encrypt("hello", "world") fmt.Println(c) fmt.Println(decrypt("hello", c)) fmt.Println(decrypt("hello", "c2932347953ad4a4-25f496d260de9c150fc9e4c6-20bc1f8439796cc914eb783b9996a8d9c32d45e2df")) } 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 charactersOriginal file line number Diff line number Diff line change @@ -1,21 +1,19 @@ /** * Encodes a utf8 string as a byte array. * @param {String} str * @returns {Uint8Array} */ function str2buf(str) { return new TextEncoder("utf-8").encode(str); } /** * Decodes a byte array as a utf8 string. * @param {Uint8Array} buffer * @returns {String} */ function buf2str(buffer) { return new TextDecoder("utf-8").decode(buffer); } /** @@ -40,31 +38,6 @@ function buf2hex(buffer) { .join(""); } /** * Given a passphrase, this generates a crypto key * using `PBKDF2` with SHA256 and 1000 iterations. @@ -101,7 +74,7 @@ function deriveKey(passphrase, salt) { */ function encrypt(passphrase, plaintext) { const iv = crypto.getRandomValues(new Uint8Array(12)); const data = str2buf(plaintext); return deriveKey(passphrase).then(([key, salt]) => crypto.subtle .encrypt({ name: "AES-GCM", iv }, key, data) @@ -120,7 +93,7 @@ function decrypt(passphrase, saltIvCipherHex) { const [salt, iv, data] = saltIvCipherHex.split("-").map(hex2buf); return deriveKey(passphrase, salt) .then(([key]) => crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data)) .then(v => buf2str(new Uint8Array(v))); } // EXAMPLE @@ -132,6 +105,6 @@ encrypt("hello", "world") decrypt( "hello", "6102677198e41d98-84c95e2d7caf6f2d4ccbfe3c-3093cef35d0dba7a24d37f7d4580b5ad83c154329c", ).then(console.log); */ 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 charactersOriginal file line number Diff line number Diff line change @@ -16,8 +16,6 @@ def encrypt(passphrase: str, plaintext: str) -> str: aes = AESGCM(key) iv = os.urandom(12) plaintext = plaintext.encode("utf8") ciphertext = aes.encrypt(iv, plaintext, None) return "%s-%s-%s" % (hexlify(salt).decode("utf8"), hexlify(iv).decode("utf8"), hexlify(ciphertext).decode("utf8")) @@ -27,12 +25,11 @@ def decrypt(passphrase: str, ciphertext: str) -> str: key, _ = deriveKey(passphrase, salt) aes = AESGCM(key) plaintext = aes.decrypt(iv, ciphertext, None) return plaintext.decode("utf8") if __name__ == "__main__": ciphertext = encrypt("hello", "world") print(ciphertext) print(decrypt("hello", ciphertext)) print(decrypt("hello", "6102677198e41d98-84c95e2d7caf6f2d4ccbfe3c-3093cef35d0dba7a24d37f7d4580b5ad83c154329c")) -
Travis Scholl revised this gist
Sep 7, 2017 . 3 changed files with 104 additions and 89 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -20,46 +20,41 @@ import ( func deriveKey(passphrase string, salt []byte) ([]byte, []byte) { if salt == nil { salt = make([]byte, 8) // http://www.ietf.org/rfc/rfc2898.txt // Salt. rand.Read(salt) } return pbkdf2.Key([]byte(passphrase), salt, 1000, 32, sha256.New), salt } func encrypt(passphrase, plaintext string) string { key, salt := deriveKey(passphrase, nil) iv := make([]byte, 12) // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf // Section 8.2 rand.Read(iv) b, _ := aes.NewCipher(key) aesgcm, _ := cipher.NewGCM(b) data := aesgcm.Seal(nil, iv, []byte(plaintext), nil) return hex.EncodeToString(salt) + "-" + hex.EncodeToString(iv) + "-" + hex.EncodeToString(data) } func decrypt(passphrase, ciphertext string) string { arr := strings.Split(ciphertext, "-") salt, _ := hex.DecodeString(arr[0]) iv, _ := hex.DecodeString(arr[1]) data, _ := hex.DecodeString(arr[2]) key, _ := deriveKey(passphrase, salt) b, _ := aes.NewCipher(key) aesgcm, _ := cipher.NewGCM(b) data, _ = aesgcm.Open(nil, iv, data, nil) return string(data) } func main() { c := encrypt("hello", "world") fmt.Println(c) fmt.Println(decrypt("hello", c)) // fmt.Println(decrypt("hello", "2e449ff2896808d3-dd0739b25a4fb37d86c3891d-2d4b30ad60881030956ef1b2547c839d7935fc28eb537455cb6388be776cd664")) } 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 charactersOriginal file line number Diff line number Diff line change @@ -2,68 +2,87 @@ const encoder = new TextEncoder("utf-8"); /** * Encodes a utf8 string as a byte array. * @param {String} str * @returns {Uint8Array} */ function str2buf(str) { return encoder.encode(str); } const decoder = new TextDecoder("utf-8"); /** * Decodes a byte array as a utf8 string. * @param {Uint8Array} buffer * @returns {String} */ function buf2str(buffer) { return decoder.decode(buffer); // TextDecoder RUINS THE BUFFER } /** * Decodes a string of hex to a byte array. * @param {String} hexStr * @returns {Uint8Array} */ function hex2buf(hexStr) { return new Uint8Array(hexStr.match(/.{2}/g).map(h => parseInt(h, 16))); } /** * Encodes a byte array as a string of hex. * @param {Uint8Array} buffer * @returns {String} */ function buf2hex(buffer) { return Array.prototype.slice .call(new Uint8Array(buffer)) .map(x => [x >> 4, x & 15]) .map(ab => ab.map(x => x.toString(16)).join("")) .join(""); } /** * Pad the end of the buffer with `0`'s * until the length is 0 mod 16. * @param {Uint8Array} buffer * @returns {Uint8Array} */ function padBuffer(buffer) { const paddedBuffer = new Uint8Array(buffer.length + (16 - buffer.length % 16) % 16); buffer.forEach((x, i) => (paddedBuffer[i] = x)); return paddedBuffer; } /** * Strip any `0`'s at the end of a Uint8Array buffer. * @param {Uint8Array} buffer * @returns {Uint8Array} */ function stripBuffer(buffer) { i = buffer.length - 1; while (buffer[i] === 0 && i >= 0) { i--; } return buffer.slice(0, i + 1); } /** * Given a passphrase, this generates a crypto key * using `PBKDF2` with SHA256 and 1000 iterations. * If no salt is given, a new one is generated. * The return value is an array of `[key, salt]`. * @param {String} passphrase * @param {UInt8Array} salt [salt=random bytes] * @returns {Promise<[CryptoKey,UInt8Array]>} */ function deriveKey(passphrase, salt) { salt = salt || crypto.getRandomValues(new Uint8Array(8)); return crypto.subtle .importKey("raw", str2buf(passphrase), "PBKDF2", false, ["deriveKey"]) .then(key => crypto.subtle.deriveKey( { name: "PBKDF2", salt, iterations: 1000, hash: "SHA-256" }, key, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"], ), @@ -74,45 +93,45 @@ function deriveKey(passPhrase, salt) { /** * Given a passphrase and some plaintext, this derives a key * (generating a new salt), and then encrypts the plaintext with the derived * key using AES-GCM. The ciphertext, salt, and iv are hex encoded and joined * by a "-". So the result is `"salt-iv-ciphertext"`. * @param {String} passphrase * @param {String} plaintext * @returns {Promise<String>} */ function encrypt(passphrase, plaintext) { const iv = crypto.getRandomValues(new Uint8Array(12)); const data = padBuffer(str2buf(plaintext)); return deriveKey(passphrase).then(([key, salt]) => crypto.subtle .encrypt({ name: "AES-GCM", iv }, key, data) .then(ciphertext => `${buf2hex(salt)}-${buf2hex(iv)}-${buf2hex(ciphertext)}`), ); } /** * Given a key and ciphertext (in the form of a string) as given by `encrypt`, * this decrypts the ciphertext and returns the original plaintext * @param {String} passphrase * @param {String} saltIvCipherHex * @returns {Promise<String>} */ function decrypt(passphrase, saltIvCipherHex) { const [salt, iv, data] = saltIvCipherHex.split("-").map(hex2buf); return deriveKey(passphrase, salt) .then(([key]) => crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data)) .then(v => buf2str(stripBuffer(new Uint8Array(v)))); } // EXAMPLE /* encrypt("hello", "world") .then(v => console.log("ENCRYPTED", v) || v) .then(v => decrypt("hello", v)) .then(v => console.log("DECRYPTED ", v) || v); decrypt( "hello", "2e449ff2896808d3-dd0739b25a4fb37d86c3891d-2d4b30ad60881030956ef1b2547c839d7935fc28eb537455cb6388be776cd664", ).then(console.log); */ 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 charactersOriginal file line number Diff line number Diff line change @@ -1,37 +1,38 @@ import hashlib import os from binascii import hexlify, unhexlify from cryptography.hazmat.primitives.ciphers.aead import AESGCM def deriveKey(passphrase: str, salt: bytes=None) -> [str, bytes]: if salt is None: salt = os.urandom(8) return hashlib.pbkdf2_hmac("sha256", passphrase.encode("utf8"), salt, 1000), salt def encrypt(passphrase: str, plaintext: str) -> str: key, salt = deriveKey(passphrase) aes = AESGCM(key) iv = os.urandom(12) plaintext = plaintext.encode("utf8") while len(plaintext) % 16 != 0: plaintext += bytes([0]) ciphertext = aes.encrypt(iv, plaintext, None) return "%s-%s-%s" % (hexlify(salt).decode("utf8"), hexlify(iv).decode("utf8"), hexlify(ciphertext).decode("utf8")) def decrypt(passphrase: str, ciphertext: str) -> str: salt, iv, ciphertext = map(unhexlify, ciphertext.split("-")) key, _ = deriveKey(passphrase, salt) aes = AESGCM(key) plaintext = aes.decrypt(iv, ciphertext, None) while plaintext[-1] == 0: plaintext = plaintext[:-1] return plaintext.decode("utf8") if __name__ == "__main__": ciphertext = encrypt("hello", "world") print(ciphertext) print(decrypt("hello", ciphertext)) -
tscholl2 revised this gist
Sep 6, 2017 . 1 changed file with 37 additions and 0 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,37 @@ import hashlib import os import pyaes # pip install pyaes from binascii import hexlify, unhexlify def deriveKey(passphrase: str, salt: bytes=None) -> [str, bytes]: if salt is None: salt = os.urandom(16) return hashlib.pbkdf2_hmac("sha256", passphrase.encode("utf8"), salt, 1000), salt def encrypt(passphrase: str, plaintext: str) -> str: key, salt = deriveKey(passphrase) iv = os.urandom(16) aes = pyaes.AESModeOfOperationCBC(key, iv=iv) plaintext = plaintext.encode("utf8") while len(plaintext) % 16 != 0: plaintext += bytes([0xb]) # TODO this isn't working ciphertext = aes.encrypt(plaintext) return "%s-%s-%s" % (hexlify(salt).decode("utf8"), hexlify(iv).decode("utf8"), hexlify(ciphertext).decode("utf8")) def decrypt(passphrase: str, ciphertext: str) -> str: salt, iv, ciphertext = map(unhexlify, ciphertext.split("-")) key, _ = deriveKey(passphrase, salt) aes = pyaes.AESModeOfOperationCBC(key, iv=iv) return aes.decrypt(ciphertext).decode("utf8") if __name__ == "__main__": passphrase = "hello" plaintext = "world" ciphertext = encrypt(passphrase, plaintext) print(ciphertext) print(decrypt(passphrase, ciphertext)) -
tscholl2 revised this gist
Sep 6, 2017 . 1 changed file with 65 additions and 0 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,65 @@ /* To double check aes.js, I wrote the same thing in go. Because of standards, we should be able to encrypt in js and decrypt in go. Seems to work fine, with some odd padding. */ package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "strings" "golang.org/x/crypto/pbkdf2" ) func deriveKey(passphrase string, salt []byte) ([]byte, []byte) { if salt == nil { salt = make([]byte, 16) rand.Read(salt) } return pbkdf2.Key([]byte(passphrase), salt, 1000, 32, sha256.New), salt } func decrypt(passphrase, ciphertext string) string { arr := strings.Split(ciphertext, "-") salt, _ := hex.DecodeString(arr[0]) iv, _ := hex.DecodeString(arr[1]) data, _ := hex.DecodeString(arr[2]) key, _ := deriveKey(passphrase, salt) b, _ := aes.NewCipher(key) mode := cipher.NewCBCDecrypter(b, iv) mode.CryptBlocks(data, data) return string(data) } func encrypt(passphrase, plaintext string) string { key, salt := deriveKey(passphrase, nil) b, _ := aes.NewCipher(key) iv := make([]byte, 16) rand.Read(iv) mode := cipher.NewCBCEncrypter(b, iv) data := []byte(plaintext) for len(data)%16 != 0 { data = append(data, 11) // I don't know why, but it seems that using web crypto you get extra 11's } mode.CryptBlocks(data, data) return hex.EncodeToString(salt) + "-" + hex.EncodeToString(iv) + "-" + hex.EncodeToString(data) } func main() { passphrase := "world" ciphertext := "de8d8b4d845c12b3085059d652403735-8e86a6ca52f4c44c744a3a0bee0c2d7d-778f133498a21f1b7aeb7c6f2e82bfd5" plaintext := decrypt(passphrase, ciphertext) fmt.Println([]byte(plaintext)) plaintext = "hello" ciphertext = encrypt(passphrase, plaintext) fmt.Println(ciphertext) fmt.Println(decrypt(passphrase, ciphertext)) } -
tscholl2 created this gist
Sep 6, 2017 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,118 @@ const encoder = new TextEncoder("utf-8"); /** * Encodes a utf8 string as a byte array. * @param {String} str * @returns {Promise<Uint8Array>} */ function str2buf(str) { return new Promise(resolve => resolve(encoder.encode(str))); } const decoder = new TextDecoder("utf-8"); /** * Decodes a byte array as a utf8 string. * @param {Uint8Array} buffer * @returns {Promise<String>} */ function buf2str(buffer) { return new Promise(resolve => resolve(decoder.decode(buffer))); } /** * Decodes a string of hex to a byte array. * @param {String} hexStr * @returns {Promise<Uint8Array>} */ function hex2buf(hexStr) { return new Promise(resolve => resolve(new Uint8Array(hexStr.match(/.{2}/g).map(h => parseInt(h, 16)))), ); } /** * Encodes a byte array as a string of hex. * @param {Uint8Array} buffer * @returns {Promise<String>} */ function buf2hex(buffer) { return new Promise(resolve => { const arr = Array.prototype.slice.call(new Uint8Array(buffer)); resolve( arr .map(x => [x >> 4, x & 15]) .map(ab => ab.map(x => x.toString(16)).join("")) .join(""), ); }); } /** * Given a passphrase, this generates a crypto key * using `PBKDF2` with SHA256 and 1000 iterations. * If no salt is given, a new one is generated. * The return value is an array of `[key, salt]`. * @param {String} passPhrase * @param {UInt8Array} salt [salt=random bytes] * @returns {Promise<[CryptoKey,UInt8Array]>} */ function deriveKey(passPhrase, salt) { salt = salt || crypto.getRandomValues(new Uint8Array(16)); return str2buf(passPhrase) .then(buffer => crypto.subtle.importKey("raw", buffer, "PBKDF2", false, ["deriveKey"])) .then(key => crypto.subtle.deriveKey( { name: "PBKDF2", salt, iterations: 1000, hash: "SHA-256" }, key, { name: "AES-CBC", length: 256 }, false, ["encrypt", "decrypt"], ), ) .then(key => [key, salt]); } /** * Given a passphrase and some plaintext, this derives a key * (generating a new salt), and then encrypts the plaintext with the derived * key using AES-CBC. The ciphertext, salt, and iv are hex encoded and joined * by a "-". So the result is `"salt-iv-ciphertext"`. * @param {String} passPhrase * @param {String} plainText * @returns {Promise<String>} */ function encrypt(passPhrase, plainText) { const iv = crypto.getRandomValues(new Uint8Array(16)); return Promise.all([deriveKey(passPhrase), str2buf(plainText)]).then(([[key, salt], data]) => crypto.subtle.encrypt({ name: "AES-CBC", iv }, key, data).then(ciphertext => { return Promise.all([buf2hex(salt), buf2hex(iv), buf2hex(ciphertext)]).then(a => a.join("-")); }), ); } /** * Given a key and ciphertext (in the form of a string) as given by `encrypt`, * this decrypts the ciphertext and returns the original plaintext * @param {String} passPhrase * @param {String} saltIvCipherHex * @returns {Promise<String>} */ function decrypt(passPhrase, saltIvCipherHex) { const [saltHex, ivHex, cipherHex] = saltIvCipherHex.split("-"); return Promise.all([ hex2buf(saltHex).then(salt => deriveKey(passPhrase, salt).then(([key, salt]) => key)), hex2buf(ivHex), hex2buf(cipherHex), ]) .then(([key, iv, data]) => crypto.subtle.decrypt({ name: "AES-CBC", iv }, key, data)) .then(buf2str); } /* // EXAMPLE const s = "hello world"; const k = "key"; encrypt(k, s) .then(v => console.log("ENCRYPTED", v) || v) .then(v => decrypt(k, v)) .then(v => console.log("DECRYPTED ", v) || v); */