check / check (push) Failing after 2s
script/lint-darwin (make lint-darwin; run by script/check, and its commands by the Dockerfile lint stage) runs go vet and golangci-lint with GOOS=darwin and cgo off. Compiling cgo for macOS needs Apple's SDK, so the three functions that call go-keychain, which is cgo there, move to keychainunlocker_cgo.go; a macOS build without cgo gets keychainunlocker_nocgo.go and the macse stub, whose errors name the missing macOS build with cgo. The rest of the keychain unlocker and its plain-Go tests are now checked; their findings are fixed without changing behaviour, and lines over 88 columns in the unchecked files are wrapped. Model: opus-5-5
119 lines
3.5 KiB
Go
119 lines
3.5 KiB
Go
//go:build darwin
|
|
|
|
//nolint:testpackage // white-box test of unexported getLongTermPrivateKey
|
|
package secret
|
|
|
|
import (
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"filippo.io/age"
|
|
"git.eeqj.de/sneak/secret/pkg/agehd"
|
|
"github.com/awnumar/memguard"
|
|
"github.com/spf13/afero"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// realVault is a minimal VaultInterface backed by a real afero filesystem,
|
|
// using the same directory layout as vault.Vault.
|
|
type realVault struct {
|
|
name string
|
|
stateDir string
|
|
fs afero.Fs
|
|
}
|
|
|
|
func (v *realVault) GetDirectory() (string, error) {
|
|
return filepath.Join(v.stateDir, "vaults.d", v.name), nil
|
|
}
|
|
func (v *realVault) GetName() string { return v.name }
|
|
|
|
//nolint:ireturn // implements VaultInterface
|
|
func (v *realVault) GetFilesystem() afero.Fs { return v.fs }
|
|
|
|
// Unused by getLongTermPrivateKey — these satisfy VaultInterface.
|
|
func (v *realVault) AddSecret(string, *memguard.LockedBuffer, bool) error {
|
|
panic("not used")
|
|
}
|
|
|
|
//nolint:ireturn // implements VaultInterface
|
|
func (v *realVault) GetCurrentUnlocker() (Unlocker, error) {
|
|
panic("not used")
|
|
}
|
|
|
|
func (v *realVault) GetOrDeriveLongTermKey() (*age.X25519Identity, error) {
|
|
panic("not used")
|
|
}
|
|
|
|
func (v *realVault) SetMnemonic(*memguard.LockedBuffer) {
|
|
panic("not used")
|
|
}
|
|
|
|
func (v *realVault) SetUnlockPassphrase(*memguard.LockedBuffer) {
|
|
panic("not used")
|
|
}
|
|
|
|
func (v *realVault) CreatePassphraseUnlocker(
|
|
*memguard.LockedBuffer,
|
|
) (*PassphraseUnlocker, error) {
|
|
panic("not used")
|
|
}
|
|
|
|
// createRealVault sets up a complete vault directory structure on an in-memory
|
|
// filesystem, identical to what vault.CreateVault produces.
|
|
func createRealVault(
|
|
t *testing.T, fs afero.Fs, stateDir, name string, derivationIndex uint32,
|
|
) *realVault {
|
|
t.Helper()
|
|
|
|
vaultDir := filepath.Join(stateDir, "vaults.d", name)
|
|
require.NoError(t, fs.MkdirAll(filepath.Join(vaultDir, "secrets.d"), DirPerms))
|
|
require.NoError(t, fs.MkdirAll(filepath.Join(vaultDir, "unlockers.d"), DirPerms))
|
|
|
|
metadata := VaultMetadata{
|
|
CreatedAt: time.Now(),
|
|
DerivationIndex: derivationIndex,
|
|
}
|
|
metaBytes, err := json.Marshal(metadata)
|
|
require.NoError(t, err)
|
|
require.NoError(t, afero.WriteFile(fs,
|
|
filepath.Join(vaultDir, "vault-metadata.json"), metaBytes, FilePerms))
|
|
|
|
return &realVault{name: name, stateDir: stateDir, fs: fs}
|
|
}
|
|
|
|
func TestGetLongTermPrivateKeyUsesVaultDerivationIndex(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
//nolint:dupword // BIP39 test mnemonic repeats words by design
|
|
const testMnemonic = "abandon abandon abandon abandon abandon abandon " +
|
|
"abandon abandon abandon abandon abandon about"
|
|
|
|
// Derive expected keys at two different indices to prove they differ.
|
|
key0, err := agehd.DeriveIdentity(testMnemonic, 0)
|
|
require.NoError(t, err)
|
|
key5, err := agehd.DeriveIdentity(testMnemonic, 5)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, key0.String(), key5.String(),
|
|
"sanity check: different derivation indices must produce different keys")
|
|
|
|
// Build a real vault with DerivationIndex=5 on an in-memory filesystem.
|
|
fs := afero.NewMemMapFs()
|
|
vault := createRealVault(t, fs, "/state", "test-vault", 5)
|
|
|
|
mnemonic := memguard.NewBufferFromBytes([]byte(testMnemonic))
|
|
defer mnemonic.Destroy()
|
|
|
|
result, err := getLongTermPrivateKey(fs, vault, mnemonic, nil)
|
|
require.NoError(t, err)
|
|
|
|
defer result.Destroy()
|
|
|
|
assert.Equal(t, key5.String(), string(result.Bytes()),
|
|
"getLongTermPrivateKey should derive at vault's DerivationIndex (5)")
|
|
assert.NotEqual(t, key0.String(), string(result.Bytes()),
|
|
"getLongTermPrivateKey must not use hardcoded index 0")
|
|
}
|