1
0
mirror of https://github.com/Luzifer/wasm-openssl.git synced 2024-09-16 14:38:28 +00:00

Extend API for the user to configure key derivation

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2020-06-13 16:17:36 +02:00
parent e0199f23d7
commit 6765123358
Signed by: luzifer
GPG Key ID: DC2729FDD34BE99E
2 changed files with 84 additions and 18 deletions

View File

@ -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"
```

72
main.go
View File

@ -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)
}