From 6765123358f64ed8b52ae94dd9460a43ef9756e7 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sat, 13 Jun 2020 16:17:36 +0200 Subject: [PATCH] Extend API for the user to configure key derivation Signed-off-by: Knut Ahlers --- README.md | 30 ++++++++++++++++++++--- main.go | 72 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 1b00c1d..712f481 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,35 @@ You will need to have `wasm_exec.js` installed in your project to load the binar For an embedding example see the `example` folder in this repo. -If you have a top-level function `opensslLoaded()` defined, this will be called in the initialization of the `openssl.wasm`. This serves as a notification you do have now access to the top-level functions `opensslEncrypt` and `opensslDecrypt`: +If you have a top-level function `opensslLoaded()` defined, this will be called in the initialization of the `openssl.wasm`. This serves as a notification you do have now access to the library functions: ```javascript -function opensslDecrypt(ciphertext, passphrase, callback) {} -function opensslEncrypt(plaintext, passphrase, callback) {} +OpenSSL.decrypt = (ciphertext, passphrase, callback, hashAlgo = OpenSSL.SHA256, usePBKDF = true, iterations = 10000) => {...} +OpenSSL.encrypt = (plaintext, passphrase, callback, hashAlgo = OpenSSL.SHA256, usePBKDF = true, iterations = 10000) => {...} ``` The functions will not return anything in the moment as in the current state Go WASM support does not have return values. Instead the callback function you've provided will be called and always have two arguments: `function callback(result, error)` - The `result` will be the plaintext on `decrypt` and the ciphertext on `encrypt`. The `error` will either be `null` or a string containing details about the error. When an error occurred the `result` is `null`. + +### Examples + +```javascript +// Encryption with default parameters +OpenSSL.encrypt('test', 'test', (res, err) => console.log(res, err)) +// U2FsdGVkX18r5Lf94A7Ng1iO03jPtKeM1hq8cZdrrww= null + +// Decryption with default parameters +OpenSSL.decrypt('U2FsdGVkX18r5Lf94A7Ng1iO03jPtKeM1hq8cZdrrww=', 'test', (res, err) => console.log(res, err)) +// test null + +// Encryption with custom parameters for key derivation +OpenSSL.encrypt('test', 'test', (res, err) => console.log(res, err), OpenSSL.SHA1, true, 25000) +// U2FsdGVkX19HCUnnaJefLxuljrhoDpCdzDOuvIqiB9Q= null + +// Decryption with the same custom parameters +OpenSSL.decrypt('U2FsdGVkX19HCUnnaJefLxuljrhoDpCdzDOuvIqiB9Q=', 'test', (res, err) => console.log(res, err), OpenSSL.SHA1, true, 25000) +// test null + +// Decryption with non-matching custom parameters (leads to an error) +OpenSSL.decrypt('U2FsdGVkX19HCUnnaJefLxuljrhoDpCdzDOuvIqiB9Q=', 'test', (res, err) => console.log(res, err), OpenSSL.SHA1, true) +// null "decrypt failed: invalid padding" +``` diff --git a/main.go b/main.go index a4d52bb..ed981a5 100644 --- a/main.go +++ b/main.go @@ -1,17 +1,35 @@ package main import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" "fmt" + "hash" "syscall/js" openssl "github.com/Luzifer/go-openssl/v4" ) -var defaultCG = openssl.PBKDF2SHA256 +var ( + hashAlgo = []func() hash.Hash{ + sha256.New, + sha1.New, + md5.New, + } +) func main() { - js.Global().Set("opensslDecrypt", js.FuncOf(decrypt)) - js.Global().Set("opensslEncrypt", js.FuncOf(encrypt)) + js.Global().Set("OpenSSL", map[string]interface{}{ + // Function definitions + "decrypt": js.FuncOf(decrypt), + "encrypt": js.FuncOf(encrypt), + + // Hash algorithm indices + "SHA256": 0, + "SHA1": 1, + "MD5": 2, + }) // Trigger custom "event" if js.Global().Get("opensslLoaded").Type() == js.TypeFunction { @@ -22,19 +40,19 @@ func main() { } func decrypt(this js.Value, i []js.Value) interface{} { - if len(i) != 3 { - println("decrypt requires 3 arguments") + if len(i) < 3 { + println("decrypt requires at least 3 arguments") return nil } var ( - ciphertext = i[0].String() - password = i[1].String() - callback = i[2] + ciphertext string = i[0].String() + password string = i[1].String() + callback js.Value = i[2] ) o := openssl.New() - plaintext, err := o.DecryptBytes(password, []byte(ciphertext), defaultCG) + plaintext, err := o.DecryptBytes(password, []byte(ciphertext), getCredentialGenerator(i)) if err != nil { callback.Invoke(nil, fmt.Sprintf("decrypt failed: %s", err)) return nil @@ -45,19 +63,19 @@ func decrypt(this js.Value, i []js.Value) interface{} { } func encrypt(this js.Value, i []js.Value) interface{} { - if len(i) != 3 { - println("encrypt requires 3 arguments") + if len(i) < 3 { + println("encrypt requires at least 3 arguments") return nil } var ( - plaintext = i[0].String() - password = i[1].String() - callback = i[2] + plaintext string = i[0].String() + password string = i[1].String() + callback js.Value = i[2] ) o := openssl.New() - ciphertext, err := o.EncryptBytes(password, []byte(plaintext), defaultCG) + ciphertext, err := o.EncryptBytes(password, []byte(plaintext), getCredentialGenerator(i)) if err != nil { callback.Invoke(nil, fmt.Sprintf("encrypt failed: %s", err)) return nil @@ -66,3 +84,27 @@ func encrypt(this js.Value, i []js.Value) interface{} { callback.Invoke(string(ciphertext), nil) return nil } + +func getCredentialGenerator(i []js.Value) openssl.CredsGenerator { + var ( + algo = hashAlgo[0] + usePBKDF = true + iterations = 10000 + ) + + switch len(i) { + case 6: + iterations = i[5].Int() + fallthrough + case 5: + usePBKDF = i[4].Bool() + fallthrough + case 4: + algo = hashAlgo[i[3].Int()] + } + + if usePBKDF { + return openssl.NewPBKDF2Generator(algo, iterations) + } + return openssl.NewBytesToKeyGenerator(algo) +}