Skip to content

Commit

Permalink
fix: use seed as key to encrypt static channel backup
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Oct 31, 2024
1 parent ae3984f commit 6a956e4
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 21 deletions.
3 changes: 1 addition & 2 deletions alby/alby_oauth_service_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package alby

import (
"encoding/hex"
"testing"

"github.com/getAlby/hub/config"
Expand Down Expand Up @@ -41,7 +40,7 @@ func TestEncryptedBackup(t *testing.T) {
encryptedChannelsBackupKey, err := appKey.NewChildKey(0)
assert.Nil(t, err)

decrypted, err := config.AesGcmDecrypt(encryptedBackup.Data, hex.EncodeToString(encryptedChannelsBackupKey.Key))
decrypted, err := config.AesGcmDecryptWithKey(encryptedBackup.Data, encryptedChannelsBackupKey.Key)
assert.Nil(t, err)

assert.Equal(t, "{\"node_id\":\"037e702144c4fa485d42f0f69864e943605823763866cf4bf619d2d2cf2eda420b\",\"channels\":[],\"monitors\":[]}\n", decrypted)
Expand Down
62 changes: 60 additions & 2 deletions config/aesgcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"strings"

"golang.org/x/crypto/argon2"
Expand All @@ -23,7 +24,7 @@ func DeriveKey(password string, salt []byte) ([]byte, []byte, error) {
return key, salt, nil
}

func AesGcmEncrypt(plaintext string, password string) (string, error) {
func AesGcmEncryptWithPassword(plaintext string, password string) (string, error) {
secretKey, salt, err := DeriveKey(password, nil)
if err != nil {
return "", err
Expand Down Expand Up @@ -51,7 +52,7 @@ func AesGcmEncrypt(plaintext string, password string) (string, error) {
return hex.EncodeToString(salt) + "-" + hex.EncodeToString(nonce) + "-" + hex.EncodeToString(ciphertext), nil
}

func AesGcmDecrypt(ciphertext string, password string) (string, error) {
func AesGcmDecryptWithPassword(ciphertext string, password string) (string, error) {
arr := strings.Split(ciphertext, "-")
salt, _ := hex.DecodeString(arr[0])
nonce, _ := hex.DecodeString(arr[1])
Expand All @@ -78,3 +79,60 @@ func AesGcmDecrypt(ciphertext string, password string) (string, error) {

return string(plaintext), nil
}

func AesGcmEncryptWithKey(plaintext string, key []byte) (string, error) {
// require a 32 bytes key (256 bits)
if len(key) != 32 {
return "", fmt.Errorf("key must be at least 32 bytes, got %d", len(key))
}

plaintextBytes := []byte(plaintext)

aes, err := aes.NewCipher(key)
if err != nil {
return "", err
}

aesgcm, err := cipher.NewGCM(aes)
if err != nil {
return "", err
}

nonce := make([]byte, aesgcm.NonceSize())
_, err = rand.Read(nonce)
if err != nil {
return "", err
}

ciphertext := aesgcm.Seal(nil, nonce, plaintextBytes, nil)

return hex.EncodeToString(nonce) + "-" + hex.EncodeToString(ciphertext), nil
}

func AesGcmDecryptWithKey(ciphertext string, key []byte) (string, error) {
// require a 32 bytes key (256 bits)
if len(key) != 32 {
return "", fmt.Errorf("key must be at least 32 bytes, got %d", len(key))
}

arr := strings.Split(ciphertext, "-")
nonce, _ := hex.DecodeString(arr[0])
data, _ := hex.DecodeString(arr[1])

aes, err := aes.NewCipher([]byte(key))
if err != nil {
return "", err
}

aesgcm, err := cipher.NewGCM(aes)
if err != nil {
return "", err
}

plaintext, err := aesgcm.Open(nil, nonce, data, nil)
if err != nil {
return "", err
}

return string(plaintext), nil
}
51 changes: 51 additions & 0 deletions config/aesgcm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package config

import (
"encoding/hex"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/tyler-smith/go-bip32"
"github.com/tyler-smith/go-bip39"
)

func TestDecryptExistingCiphertextWithPassword(t *testing.T) {
value, err := AesGcmDecryptWithPassword("323f41394d3175b72454ccae9c0081f94df5fb4c2fb0b9283a87e5aafba81839-c335b9eeea75c28a6f823354-5055b90dadbdd01c52fbdbb7efb80609e4410357481651e89ceb1501c8e1dea1f33a8e3322a1cef4f641773667423bca5154dfeccac390cfcd719b36965adc3e6ae56fd5d6c82819596e9ef4ff07193ae345eb291fa412a1ce6066864b", "123")
assert.NoError(t, err)
assert.Equal(t, "connect maximum march lava ignore resist visa kind kiwi kidney develop animal", value)
}

func TestEncryptDecryptWithPassword(t *testing.T) {
mnemonic := "connect maximum march lava ignore resist visa kind kiwi kidney develop animal"
encrypted, err := AesGcmEncryptWithPassword(mnemonic, "123")
assert.NoError(t, err)
value, err := AesGcmDecryptWithPassword(encrypted, "123")
assert.NoError(t, err)
assert.Equal(t, mnemonic, value)
}

func TestDecryptExistingCiphertextWithKey(t *testing.T) {
mnemonic := "connect maximum march lava ignore resist visa kind kiwi kidney develop animal"
masterKey, err := bip32.NewMasterKey(bip39.NewSeed(mnemonic, ""))
assert.NoError(t, err)
value, err := AesGcmDecryptWithKey("22ad485dea4f49696594c7c4-afe35ce65fc5a45249bf1b9078472fb28395fc88c30a79c76c7d8d37cf", masterKey.Key)
assert.NoError(t, err)
assert.Equal(t, "Hello, world!", value)
}

func TestEncryptDecryptWithKey(t *testing.T) {
plaintext := "Hello, world!"
mnemonic := "connect maximum march lava ignore resist visa kind kiwi kidney develop animal"
masterKey, err := bip32.NewMasterKey(bip39.NewSeed(mnemonic, ""))
assert.NoError(t, err)

assert.Equal(t, "409e902eafba273b21dff921f0eb4bec6cbb0b657fdce8d245ca78d2920f8b73", hex.EncodeToString(masterKey.Key))

encrypted, err := AesGcmEncryptWithKey(plaintext, masterKey.Key)
fmt.Print(encrypted)
assert.NoError(t, err)
value, err := AesGcmDecryptWithKey(encrypted, masterKey.Key)
assert.NoError(t, err)
assert.Equal(t, plaintext, value)
}
4 changes: 2 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (cfg *config) get(key string, encryptionKey string, gormDB *gorm.DB) (strin

value := userConfig.Value
if userConfig.Value != "" && encryptionKey != "" && userConfig.Encrypted {
decrypted, err := AesGcmDecrypt(value, encryptionKey)
decrypted, err := AesGcmDecryptWithPassword(value, encryptionKey)
if err != nil {
return "", err
}
Expand All @@ -165,7 +165,7 @@ func (cfg *config) get(key string, encryptionKey string, gormDB *gorm.DB) (strin

func (cfg *config) set(key string, value string, clauses clause.OnConflict, encryptionKey string, gormDB *gorm.DB) error {
if encryptionKey != "" {
encrypted, err := AesGcmEncrypt(value, encryptionKey)
encrypted, err := AesGcmEncryptWithPassword(value, encryptionKey)
if err != nil {
return fmt.Errorf("failed to encrypt: %v", err)
}
Expand Down
29 changes: 14 additions & 15 deletions service/keys/keys.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package keys

import (
"encoding/hex"

"github.com/getAlby/hub/config"
"github.com/getAlby/hub/logger"
"github.com/nbd-wtf/go-nostr"
Expand All @@ -21,9 +19,9 @@ type Keys interface {
}

type keys struct {
nostrSecretKey string
nostrPublicKey string
encryptedChannelsBackupKey string
nostrSecretKey string
nostrPublicKey string
appKey *bip32.Key
}

func NewKeys() *keys {
Expand Down Expand Up @@ -68,14 +66,7 @@ func (keys *keys) Init(cfg config.Config, encryptionKey string) error {
logger.Logger.WithError(err).Error("Failed to create seed from mnemonic")
return err
}

ENCRYPTED_SCB_INDEX := uint32(0) // TODO: choose an index
encryptedChannelsBackupKey, err := appKey.NewChildKey(ENCRYPTED_SCB_INDEX)
if err != nil {
logger.Logger.WithError(err).Error("Failed to create seed from mnemonic")
return err
}
keys.encryptedChannelsBackupKey = hex.EncodeToString(encryptedChannelsBackupKey.Key)
keys.appKey = appKey
}

return nil
Expand All @@ -89,7 +80,15 @@ func (keys *keys) GetNostrSecretKey() string {
return keys.nostrSecretKey
}

// TODO: move somewhere else
func (keys *keys) EncryptChannelBackupData(channelBackupData string) (string, error) {
// FIXME: this is not the right way to encrypt the data (we already have a key, no need to generate a new one)
return config.AesGcmEncrypt(channelBackupData, keys.encryptedChannelsBackupKey)

ENCRYPTED_SCB_INDEX := uint32(0) // TODO: choose an index
encryptedChannelsBackupKey, err := keys.appKey.NewChildKey(ENCRYPTED_SCB_INDEX)
if err != nil {
logger.Logger.WithError(err).Error("Failed to create seed from mnemonic")
return "", err
}

return config.AesGcmEncryptWithKey(channelBackupData, encryptedChannelsBackupKey.Key)
}

0 comments on commit 6a956e4

Please sign in to comment.