Files
yovision/Sense/server/app/sense/device/crypto.go
T

81 lines
2.1 KiB
Go

package device
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io"
)
type CredentialVault struct{ key []byte }
func NewCredentialVault(encodedKey string, allowRandom bool) (*CredentialVault, error) {
var key []byte
if encodedKey != "" {
decoded, err := base64.StdEncoding.DecodeString(encodedKey)
if err != nil {
return nil, fmt.Errorf("SENSE_CREDENTIAL_KEY must be base64 encoded")
}
key = decoded
} else if allowRandom {
key = make([]byte, 32)
if _, err := rand.Read(key); err != nil {
return nil, err
}
}
if len(key) != 32 {
return nil, fmt.Errorf("SENSE_CREDENTIAL_KEY must decode to 32 bytes")
}
return &CredentialVault{key: key}, nil
}
func (v *CredentialVault) Encrypt(username, password string) ([]byte, error) {
plaintext, err := json.Marshal(map[string]string{"username": username, "password": password})
if err != nil {
return nil, err
}
block, err := aes.NewCipher(v.key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, plaintext, []byte("sense-device-credential-v1")), nil
}
func (v *CredentialVault) Decrypt(ciphertext []byte) (string, string, error) {
block, err := aes.NewCipher(v.key)
if err != nil {
return "", "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", "", err
}
if len(ciphertext) < gcm.NonceSize() {
return "", "", fmt.Errorf("invalid credential ciphertext")
}
nonce, encrypted := ciphertext[:gcm.NonceSize()], ciphertext[gcm.NonceSize():]
plaintext, err := gcm.Open(nil, nonce, encrypted, []byte("sense-device-credential-v1"))
if err != nil {
return "", "", fmt.Errorf("decrypt device credential: %w", err)
}
var value struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := json.Unmarshal(plaintext, &value); err != nil {
return "", "", err
}
return value.Username, value.Password, nil
}