From ebf30c759fd01fc74caa53ffad38e425398bfe46 Mon Sep 17 00:00:00 2001 From: Wim Date: Sun, 13 Sep 2020 20:11:26 +0200 Subject: [PATCH 1/2] Add support for entropy --- dpapi.go | 72 +++++++++++++++++++++++++++++++++++++++++---------- dpapi_test.go | 59 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 15 deletions(-) diff --git a/dpapi.go b/dpapi.go index 4ceda0c..7dc7af5 100644 --- a/dpapi.go +++ b/dpapi.go @@ -45,13 +45,17 @@ func (b *dataBlob) toByteArray() []byte { // Encrypt a string value to a base64 string func Encrypt(secret string) (string, error) { - return encrypt(secret, cryptProtectUIForbidden) + return encrypt(secret, "", cryptProtectUIForbidden) } -func encrypt(secret string, cf cryptProtect) (string, error) { +func EncryptEntropy(secret, entropy string) (string, error) { + return encrypt(secret, entropy, cryptProtectUIForbidden) +} + +func encrypt(secret, entropy string, cf cryptProtect) (string, error) { var result string var b []byte - b, err := encryptBytes([]byte(secret), cf) + b, err := encryptBytes([]byte(secret), []byte(entropy), cf) if err != nil { return result, errors.Wrap(err, "encryptbytes") } @@ -61,12 +65,25 @@ func encrypt(secret string, cf cryptProtect) (string, error) { // EncryptBytes encrypts a byte array and returns a byte array func EncryptBytes(data []byte) ([]byte, error) { - return encryptBytes(data, cryptProtectUIForbidden) + return encryptBytes(data, []byte{}, cryptProtectUIForbidden) } -func encryptBytes(data []byte, cf cryptProtect) ([]byte, error) { - var outblob dataBlob - r, _, err := procEncryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, 0, 0, 0, uintptr(cf), uintptr(unsafe.Pointer(&outblob))) +func EncryptBytesEntropy(data, entropy []byte) ([]byte, error) { + return encryptBytes(data, entropy, cryptProtectUIForbidden) +} + +func encryptBytes(data []byte, entropy []byte, cf cryptProtect) ([]byte, error) { + var ( + outblob dataBlob + r uintptr + err error + ) + + if len(entropy) > 0 { + r, _, err = procEncryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, uintptr(unsafe.Pointer(newBlob(entropy))), 0, 0, uintptr(cf), uintptr(unsafe.Pointer(&outblob))) + } else { + r, _, err = procEncryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, 0, 0, 0, uintptr(cf), uintptr(unsafe.Pointer(&outblob))) + } if r == 0 { return nil, errors.Wrap(err, "procencryptdata") } @@ -77,19 +94,35 @@ func encryptBytes(data []byte, cf cryptProtect) ([]byte, error) { // EncryptBytesMachineLocal encrypts a byte array and returns a byte array and associates the data // encrypted with the current computer instead of with an individual user. func EncryptBytesMachineLocal(data []byte) ([]byte, error) { - return encryptBytes(data, cryptProtectUIForbidden|cryptProtectLocalMachine) + return encryptBytes(data, []byte{}, cryptProtectUIForbidden|cryptProtectLocalMachine) +} + +func EncryptBytesMachineLocalEntropy(data, entropy []byte) ([]byte, error) { + return encryptBytes(data, entropy, cryptProtectUIForbidden|cryptProtectLocalMachine) } // EncryptMachineLocal a string value to a base64 string and associates the data encrypted with the // current computer instead of with an individual user. func EncryptMachineLocal(secret string) (string, error) { - return encrypt(secret, cryptProtectUIForbidden|cryptProtectLocalMachine) + return encrypt(secret, "", cryptProtectUIForbidden|cryptProtectLocalMachine) +} + +func EncryptMachineLocalEntropy(secret, entropy string) (string, error) { + return encrypt(secret, entropy, cryptProtectUIForbidden|cryptProtectLocalMachine) } // DecryptBytes decrypts a byte array returning a byte array -func DecryptBytes(data []byte) ([]byte, error) { - var outblob dataBlob - r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, 0, 0, 0, uintptr(cryptProtectUIForbidden), uintptr(unsafe.Pointer(&outblob))) +func decryptBytes(data, entropy []byte, cf cryptProtect) ([]byte, error) { + var ( + outblob dataBlob + r uintptr + err error + ) + if len(entropy) > 0 { + r, _, err = procDecryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, uintptr(unsafe.Pointer(newBlob(entropy))), 0, 0, uintptr(cf), uintptr(unsafe.Pointer(&outblob))) + } else { + r, _, err = procDecryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, 0, 0, 0, uintptr(cf), uintptr(unsafe.Pointer(&outblob))) + } if r == 0 { return nil, errors.Wrap(err, "procdecryptdata") } @@ -99,12 +132,25 @@ func DecryptBytes(data []byte) ([]byte, error) { // Decrypt a string to a string func Decrypt(data string) (string, error) { + return DecryptEntropy(data, "") +} + +// EncryptBytes encrypts a byte array and returns a byte array +func DecryptBytes(data []byte) ([]byte, error) { + return decryptBytes(data, []byte{}, cryptProtectUIForbidden) +} + +func DecryptBytesEntropy(data, entropy []byte) ([]byte, error) { + return decryptBytes(data, entropy, cryptProtectUIForbidden) +} + +func DecryptEntropy(data, entropy string) (string, error) { raw, err := base64.StdEncoding.DecodeString(data) if err != nil { return "", errors.Wrap(err, "decodestring") } - b, err := DecryptBytes(raw) + b, err := decryptBytes(raw, []byte(entropy), cryptProtectUIForbidden) if err != nil { return "", errors.Wrap(err, "decryptbytes") } diff --git a/dpapi_test.go b/dpapi_test.go index 84931e7..91b9855 100644 --- a/dpapi_test.go +++ b/dpapi_test.go @@ -9,6 +9,8 @@ import ( func TestString(t *testing.T) { secret := "Hello World!;" + entropy := "something" + enc, err := Encrypt(secret) if err != nil { t.Error("err from Encrypt: ", err) @@ -20,11 +22,24 @@ func TestString(t *testing.T) { if dec != secret { t.Errorf("expected: '%s' got: '%s'", secret, dec) } + + enc, err = EncryptEntropy(secret, entropy) + if err != nil { + t.Error("err from EncryptEntropy: ", err) + } + dec, err = DecryptEntropy(enc, entropy) + if err != nil { + t.Error("err from DecryptEntropy: ", err) + } + if dec != secret { + t.Errorf("expected: '%s' got: '%s'", secret, dec) + } } func TestBytes(t *testing.T) { secret := []byte("Hello World!;") + entropy := []byte("something") enc, err := EncryptBytes(secret) if err != nil { t.Error("err from EncryptBytes: ", err) @@ -37,11 +52,25 @@ func TestBytes(t *testing.T) { if c != 0 { t.Errorf("expected: '%s' got: '%s'", hex.EncodeToString(secret), hex.EncodeToString(dec)) } + + enc, err = EncryptBytesEntropy(secret, entropy) + if err != nil { + t.Error("err from EncryptBytesEntropy: ", err) + } + dec, err = DecryptBytesEntropy(enc, entropy) + if err != nil { + t.Error("err from DecryptBytesEntropy: ", err) + } + c = bytes.Compare(dec, secret) + if c != 0 { + t.Errorf("expected: '%s' got: '%s'", hex.EncodeToString(secret), hex.EncodeToString(dec)) + } } func TestMachineLocalString(t *testing.T) { secret := "Hello World!;" + entropy := "something" enc, err := EncryptMachineLocal(secret) if err != nil { t.Error("err from Encrypt: ", err) @@ -53,11 +82,24 @@ func TestMachineLocalString(t *testing.T) { if dec != secret { t.Errorf("expected: '%s' got: '%s'", secret, dec) } + + enc, err = EncryptMachineLocalEntropy(secret, entropy) + if err != nil { + t.Error("err from EncryptMachineLocaleEntropy: ", err) + } + dec, err = DecryptEntropy(enc, entropy) + if err != nil { + t.Error("err from DecryptEntropy: ", err) + } + if dec != secret { + t.Errorf("expected: '%s' got: '%s'", secret, dec) + } } func TestMachineLocalBytes(t *testing.T) { - + secret := []byte("Hello World!;") + entropy := []byte("something") enc, err := EncryptBytesMachineLocal(secret) if err != nil { t.Error("err from EncryptBytesMachineLocal: ", err) @@ -70,4 +112,17 @@ func TestMachineLocalBytes(t *testing.T) { if c != 0 { t.Errorf("expected: '%s' got: '%s'", hex.EncodeToString(secret), hex.EncodeToString(dec)) } -} \ No newline at end of file + + enc, err = EncryptBytesMachineLocalEntropy(secret, entropy) + if err != nil { + t.Error("err from EncryptBytesMachineLocalEntropy: ", err) + } + dec, err = DecryptBytesEntropy(enc, entropy) + if err != nil { + t.Error("err from DecryptBytesEntropy: ", err) + } + c = bytes.Compare(dec, secret) + if c != 0 { + t.Errorf("expected: '%s' got: '%s'", hex.EncodeToString(secret), hex.EncodeToString(dec)) + } +} From a6aaeb7f63169d98126360569bc2415c2c5996e9 Mon Sep 17 00:00:00 2001 From: Wim Date: Mon, 14 Sep 2020 20:21:41 +0200 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Daniel Cormier --- dpapi.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dpapi.go b/dpapi.go index 7dc7af5..f56be15 100644 --- a/dpapi.go +++ b/dpapi.go @@ -65,7 +65,7 @@ func encrypt(secret, entropy string, cf cryptProtect) (string, error) { // EncryptBytes encrypts a byte array and returns a byte array func EncryptBytes(data []byte) ([]byte, error) { - return encryptBytes(data, []byte{}, cryptProtectUIForbidden) + return encryptBytes(data, nil, cryptProtectUIForbidden) } func EncryptBytesEntropy(data, entropy []byte) ([]byte, error) { @@ -94,7 +94,7 @@ func encryptBytes(data []byte, entropy []byte, cf cryptProtect) ([]byte, error) // EncryptBytesMachineLocal encrypts a byte array and returns a byte array and associates the data // encrypted with the current computer instead of with an individual user. func EncryptBytesMachineLocal(data []byte) ([]byte, error) { - return encryptBytes(data, []byte{}, cryptProtectUIForbidden|cryptProtectLocalMachine) + return encryptBytes(data, nil, cryptProtectUIForbidden|cryptProtectLocalMachine) } func EncryptBytesMachineLocalEntropy(data, entropy []byte) ([]byte, error) { @@ -137,7 +137,7 @@ func Decrypt(data string) (string, error) { // EncryptBytes encrypts a byte array and returns a byte array func DecryptBytes(data []byte) ([]byte, error) { - return decryptBytes(data, []byte{}, cryptProtectUIForbidden) + return decryptBytes(data, nil, cryptProtectUIForbidden) } func DecryptBytesEntropy(data, entropy []byte) ([]byte, error) {