forked from TrueCloudLab/lego
Vitaliy Potyarkin
9ff9d5be25
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>
190 lines
4.8 KiB
Go
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))
|
|
}
|
|
}
|