lego/providers/http/frostfs/client_test.go
Vitaliy Potyarkin 9ff9d5be25 frostfs: Fix invalid signatures issued by key from json
Ephemeral keys worked fine while keys loaded from filesystem would
generate invalid signatures. This was caused by destroying private key
material during calls to Wallet.Close() and Account.Close(). Since these
calls do nothing except wiping the private key, we omit them now.
Responsibility for private key security is delegated to caller of getKey()

Signed-off-by: Vitaliy Potyarkin <v.potyarkin@yadro.com>
2024-10-16 12:05:26 +03:00

190 lines
4.8 KiB
Go

package frostfs
// Tests for our FrostFS client code
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/sha256"
"os"
"sync"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
// Initialize storage backend for tests.
func openStorage(t *testing.T) *Storage {
t.Helper()
cid := os.Getenv("FROSTFS_CONTAINER") // sample: "348WWfBKbS79Wbmm38MRE3oBoEDM5Ga1XXbGKGNyisDM"
endpoint := os.Getenv("FROSTFS_ENDPOINT") // sample: "grpc://localhost:8802"
if cid == "" || endpoint == "" {
t.Skipf("one or more environment variables not set: FROSTFS_ENDPOINT, FROSTFS_CONTAINER")
}
key, err := getKey(
os.Getenv("FROSTFS_WALLET"),
os.Getenv("FROSTFS_WALLET_ACCOUNT"),
os.Getenv("FROSTFS_WALLET_PASSWORD"),
)
if err != nil {
t.Fatal(err)
}
storage, err := Open(endpoint, cid, key)
if err != nil {
t.Fatal(err)
}
return storage
}
// Save some bytes to FrostFS and clean up after.
func TestObjectPutDelete(t *testing.T) {
payload := []byte("Hello FrostFS!\n")
storage := openStorage(t)
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
oid, err := storage.Save(ctx, payload, "FileName", "hello_from_sdk.txt", "Tag", "foobar")
if err != nil {
t.Fatal(err)
}
t.Logf("saved object: %s", oid)
err = storage.Delete(ctx, oid)
if err != nil {
t.Fatal(err)
}
t.Logf("deleted object: %s", oid)
}
// Check that FrostFS is in fact a content addressable storage.
func TestMulipleObjects(t *testing.T) {
payload := []byte("Multiple objects with same content should get the same object ID")
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
var wg sync.WaitGroup
objects := make(chan string)
go func() {
baseline := ""
for {
select {
case <-ctx.Done():
return
case obj, ok := <-objects:
if !ok {
return
}
if baseline == "" {
baseline = obj
continue
}
if obj != baseline {
t.Errorf("non-identical object id: %s != %s", baseline, obj)
return
}
}
}
}()
const testCount = 5
storage := openStorage(t)
for i := 0; i < testCount; i++ { //nolint:intrange
wg.Add(1)
go func() {
defer wg.Done()
oid, err := storage.Save(ctx, payload, "FileName", "CAS.txt", "Tag", "test")
if err != nil {
t.Error(err)
return
}
err = storage.Delete(ctx, oid)
if err != nil {
t.Error(err)
return
}
select {
case objects <- oid:
case <-ctx.Done():
return
}
t.Log(oid)
}()
}
wg.Wait()
}
// Check opening wallet from file system.
func TestLoadWallet(t *testing.T) {
const (
walletPath = "client_test_wallet.json"
walletAccount = "NWZnjbTKbzwtX6w1q5R3kbEKrnJ5bp1kn7"
)
key, err := getKey(walletPath, "", "")
if err != nil {
t.Fatal(err)
}
if key2addr(key) != walletAccount {
t.Fatalf("incorrect address for default account: want %s, got %s", walletAccount, key2addr(key))
}
key, err = getKey(walletPath, walletAccount, "")
if err != nil {
t.Fatal(err)
}
if key2addr(key) != walletAccount {
t.Fatalf("incorrect address for specific account: want %s, got %s", walletAccount, key2addr(key))
}
}
func key2addr(k *ecdsa.PrivateKey) string {
var owner user.ID
user.IDFromKey(&owner, k.PublicKey)
return owner.String()
}
// Check that loaded wallet key generates valid signature.
func TestLoadWalletAndSign(t *testing.T) {
const (
walletPath = "client_test_wallet.json"
walletAccount = "NWZnjbTKbzwtX6w1q5R3kbEKrnJ5bp1kn7"
walletAccountPassword = ""
walletAccountIndex = 0
dataString = "some data to sign in this test"
)
w, err := wallet.NewWalletFromFile(walletPath)
if err != nil {
t.Fatalf("wallet from file: %v", err)
}
accountFromWallet := w.Accounts[walletAccountIndex]
err = accountFromWallet.Decrypt(walletAccountPassword, w.Scrypt)
if err != nil {
t.Fatalf("wallet decrypt: %v", err)
}
key, err := getKey(walletPath, walletAccount, walletAccountPassword)
if err != nil {
t.Fatal(err)
}
wrappedKey := &keys.PrivateKey{PrivateKey: *key}
accountFromKey := wallet.NewAccountFromPrivateKey(wrappedKey)
if !accountFromKey.PublicKey().Equal(accountFromWallet.PublicKey()) {
t.Fatalf("corrupted key: want %s, got %s", accountFromWallet.PublicKey().Address(), accountFromKey.PublicKey().Address())
}
data := []byte(dataString)
sig := accountFromKey.PrivateKey().Sign(data)
hash := sha256.Sum256(data)
hashSig := accountFromKey.PrivateKey().SignHash(hash)
if !bytes.Equal(sig, hashSig) {
t.Fatalf("different signatures for data (%x) and for hash (%x)", sig, hashSig)
}
ok := accountFromKey.PublicKey().Verify(sig, hash[:])
if !ok {
t.Errorf("signature %x (%d bytes): check failed: signing key", sig, len(sig))
}
ok = accountFromWallet.PublicKey().Verify(sig, hash[:])
if !ok {
t.Errorf("signature %x (%d bytes): check failed: wallet key", sig, len(sig))
}
}