1
0
mirror of https://github.com/gilbertchen/duplicacy synced 2025-12-06 00:03:38 +00:00
Files
duplicacy/src/duplicacy_keyring_windows.go

171 lines
4.0 KiB
Go

// Copyright (c) Acrosync LLC. All rights reserved.
// Free for personal use and commercial trial
// Commercial use requires per-user licenses available from https://duplicacy.com
package duplicacy
import (
"encoding/json"
"io/ioutil"
"syscall"
"unsafe"
)
var keyringFile string
var (
dllcrypt32 = syscall.NewLazyDLL("Crypt32.dll")
dllkernel32 = syscall.NewLazyDLL("Kernel32.dll")
procEncryptData = dllcrypt32.NewProc("CryptProtectData")
procDecryptData = dllcrypt32.NewProc("CryptUnprotectData")
procLocalFree = dllkernel32.NewProc("LocalFree")
)
type DATA_BLOB struct {
cbData uint32
pbData *byte
}
func SetKeyringFile(path string) {
keyringFile = path
}
func keyringEncrypt(value []byte) ([]byte, error) {
dataIn := DATA_BLOB{
pbData: &value[0],
cbData: uint32(len(value)),
}
dataOut := DATA_BLOB{}
r, _, err := procEncryptData.Call(uintptr(unsafe.Pointer(&dataIn)),
0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&dataOut)))
if r == 0 {
return nil, err
}
address := uintptr(unsafe.Pointer(dataOut.pbData))
defer procLocalFree.Call(address)
encryptedData := make([]byte, dataOut.cbData)
for i := 0; i < len(encryptedData); i++ {
encryptedData[i] = *(*byte)(unsafe.Pointer(uintptr(int(address) + i)))
}
return encryptedData, nil
}
func keyringDecrypt(value []byte) ([]byte, error) {
dataIn := DATA_BLOB{
pbData: &value[0],
cbData: uint32(len(value)),
}
dataOut := DATA_BLOB{}
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(&dataIn)),
0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&dataOut)))
if r == 0 {
return nil, err
}
address := uintptr(unsafe.Pointer(dataOut.pbData))
defer procLocalFree.Call(address)
decryptedData := make([]byte, dataOut.cbData)
for i := 0; i < len(decryptedData); i++ {
address := int(uintptr(unsafe.Pointer(dataOut.pbData)))
decryptedData[i] = *(*byte)(unsafe.Pointer(uintptr(int(address) + i)))
}
return decryptedData, nil
}
func keyringGet(key string) (value string) {
if keyringFile == "" {
LOG_DEBUG("KEYRING_NOT_INITIALIZED", "Keyring file not set")
return ""
}
description, err := ioutil.ReadFile(keyringFile)
if err != nil {
LOG_DEBUG("KEYRING_READ", "Keyring file not read: %v", err)
return ""
}
var keyring map[string][]byte
err = json.Unmarshal(description, &keyring)
if err != nil {
LOG_DEBUG("KEYRING_PARSE", "Failed to parse the keyring storage file %s: %v", keyringFile, err)
return ""
}
encryptedValue := keyring[key]
if len(encryptedValue) == 0 {
return ""
}
valueInBytes, err := keyringDecrypt(encryptedValue)
if err != nil {
LOG_DEBUG("KEYRING_DECRYPT", "Failed to decrypt the value: %v", err)
return ""
}
return string(valueInBytes)
}
func keyringSet(key string, value string) bool {
if value == "" {
return false
}
if keyringFile == "" {
LOG_DEBUG("KEYRING_NOT_INITIALIZED", "Keyring file not set")
return false
}
keyring := make(map[string][]byte)
description, err := ioutil.ReadFile(keyringFile)
if err == nil {
err = json.Unmarshal(description, &keyring)
if err != nil {
LOG_DEBUG("KEYRING_PARSE", "Failed to parse the keyring storage file %s: %v", keyringFile, err)
}
}
if value == "" {
keyring[key] = nil
} else {
// Check if the value to be set is the same as the existing one
existingEncryptedValue := keyring[key]
if len(existingEncryptedValue) > 0 {
existingValue, err := keyringDecrypt(existingEncryptedValue)
if err == nil && string(existingValue) == value {
return true
}
}
encryptedValue, err := keyringEncrypt([]byte(value))
if err != nil {
LOG_DEBUG("KEYRING_ENCRYPT", "Failed to encrypt the value: %v", err)
return false
}
keyring[key] = encryptedValue
}
description, err = json.MarshalIndent(keyring, "", " ")
if err != nil {
LOG_DEBUG("KEYRING_MARSHAL", "Failed to marshal the keyring storage: %v", err)
return false
}
err = ioutil.WriteFile(keyringFile, description, 0600)
if err != nil {
LOG_DEBUG("KEYRING_WRITE", "Failed to save the keyring storage to file %s: %v", keyringFile, err)
return false
}
return true
}