Files

175 lines
6.2 KiB
Go

package security
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"errors"
"io"
"net"
"os"
"strings"
"testing"
)
type alwaysErrorReader struct{ err error }
func (r alwaysErrorReader) Read([]byte) (int, error) { return 0, r.err }
func TestCrossPlatformCoverageEncryptRandomSourceErrors(t *testing.T) {
old := cryptoRandReader
t.Cleanup(func() { cryptoRandReader = old })
wantErr := errors.New("entropy unavailable")
cryptoRandReader = alwaysErrorReader{err: wantErr}
if _, err := Encrypt([]byte("data"), []byte("password")); !errors.Is(err, wantErr) {
t.Fatalf("salt error = %v", err)
}
cryptoRandReader = io.MultiReader(bytes.NewReader(make([]byte, SaltSize)), alwaysErrorReader{err: wantErr})
if _, err := Encrypt([]byte("data"), []byte("password")); !errors.Is(err, wantErr) {
t.Fatalf("nonce error = %v", err)
}
}
func TestCrossPlatformCoverageCryptoConstructorErrors(t *testing.T) {
wantErr := errors.New("cipher construction failed")
failCipher := func([]byte) (cipher.Block, error) {
return nil, wantErr
}
failGCM := func(cipher.Block, int) (cipher.AEAD, error) {
return nil, wantErr
}
if _, err := encryptWithFactories([]byte("data"), []byte("password"), failCipher, cipher.NewGCMWithNonceSize); !errors.Is(err, wantErr) {
t.Fatalf("Encrypt cipher error = %v", err)
}
if _, err := encryptWithFactories([]byte("data"), []byte("password"), aes.NewCipher, failGCM); !errors.Is(err, wantErr) {
t.Fatalf("Encrypt GCM error = %v", err)
}
encrypted, err := Encrypt([]byte("data"), []byte("password"))
if err != nil {
t.Fatalf("Encrypt fixture error = %v", err)
}
if _, err := decryptWithFactories(encrypted, []byte("password"), failCipher, cipher.NewGCMWithNonceSize); !errors.Is(err, wantErr) {
t.Fatalf("Decrypt cipher error = %v", err)
}
if _, err := decryptWithFactories(encrypted, []byte("password"), aes.NewCipher, failGCM); !errors.Is(err, wantErr) {
t.Fatalf("Decrypt GCM error = %v", err)
}
}
func TestCrossPlatformCoverageGetMACAddressEnumerationError(t *testing.T) {
old := networkInterfaces
t.Cleanup(func() { networkInterfaces = old })
wantErr := errors.New("enumeration failed")
networkInterfaces = func() ([]net.Interface, error) { return nil, wantErr }
if _, err := GetMACAddress(); !errors.Is(err, wantErr) {
t.Fatalf("GetMACAddress error = %v", err)
}
}
type scriptedTokenTempFile struct {
writeErr error
syncErr error
closeErr error
}
func (f *scriptedTokenTempFile) Write(p []byte) (int, error) {
if f.writeErr != nil {
return 0, f.writeErr
}
return len(p), nil
}
func (f *scriptedTokenTempFile) Sync() error { return f.syncErr }
func (f *scriptedTokenTempFile) Close() error { return f.closeErr }
func TestCrossPlatformCoverageSecureTokenStorageFailureBoundaries(t *testing.T) {
oldMkdir, oldMarshal, oldEncrypt := mkdirTokenDir, marshalTokenData, encryptTokenData
oldOpen, oldRemove, oldRename := openTokenTempFile, removeTokenFile, renameTokenFile
oldRead, oldDecrypt, oldUnmarshal := readTokenFile, decryptTokenData, unmarshalTokenData
t.Cleanup(func() {
mkdirTokenDir, marshalTokenData, encryptTokenData = oldMkdir, oldMarshal, oldEncrypt
openTokenTempFile, removeTokenFile, renameTokenFile = oldOpen, oldRemove, oldRename
readTokenFile, decryptTokenData, unmarshalTokenData = oldRead, oldDecrypt, oldUnmarshal
})
wantErr := errors.New("synthetic storage failure")
storage := NewSecureTokenStorage("config", "fallback", "mac")
data := &TokenData{AccessToken: "token"}
resetSave := func() *scriptedTokenTempFile {
file := &scriptedTokenTempFile{}
mkdirTokenDir = func(string, os.FileMode) error { return nil }
marshalTokenData = func(any) ([]byte, error) { return []byte(`{}`), nil }
encryptTokenData = func([]byte, []byte) ([]byte, error) { return []byte("encrypted"), nil }
openTokenTempFile = func(string, int, os.FileMode) (tokenTempFile, error) { return file, nil }
removeTokenFile = func(string) error { return nil }
renameTokenFile = func(string, string) error { return nil }
return file
}
resetSave()
mkdirTokenDir = func(string, os.FileMode) error { return wantErr }
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("mkdir error = %v", err)
}
resetSave()
marshalTokenData = func(any) ([]byte, error) { return nil, wantErr }
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("marshal error = %v", err)
}
resetSave()
encryptTokenData = func([]byte, []byte) ([]byte, error) { return nil, wantErr }
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("encrypt error = %v", err)
}
resetSave()
openTokenTempFile = func(string, int, os.FileMode) (tokenTempFile, error) { return nil, wantErr }
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("open error = %v", err)
}
file := resetSave()
file.writeErr = wantErr
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("write error = %v", err)
}
file = resetSave()
file.syncErr = wantErr
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("sync error = %v", err)
}
file = resetSave()
file.closeErr = wantErr
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("close error = %v", err)
}
resetSave()
renameTokenFile = func(string, string) error { return wantErr }
if err := storage.SaveToken(data); !errors.Is(err, wantErr) {
t.Fatalf("rename error = %v", err)
}
readTokenFile = func(string) ([]byte, error) { return []byte("encrypted"), nil }
decryptTokenData = func([]byte, []byte) ([]byte, error) { return nil, wantErr }
if _, err := storage.LoadToken(); !errors.Is(err, wantErr) {
t.Fatalf("decrypt error = %v", err)
}
decryptTokenData = func([]byte, []byte) ([]byte, error) { return []byte(`{}`), nil }
unmarshalTokenData = func([]byte, any) error { return wantErr }
if _, err := storage.LoadToken(); !errors.Is(err, wantErr) {
t.Fatalf("unmarshal error = %v", err)
}
removeTokenFile = func(path string) error {
if strings.HasSuffix(path, ".tmp") {
return nil
}
return wantErr
}
if err := storage.DeleteToken(); !errors.Is(err, wantErr) {
t.Fatalf("DeleteToken error = %v", err)
}
if err := DeleteEncryptedData("config", "fallback"); !errors.Is(err, wantErr) {
t.Fatalf("DeleteEncryptedData error = %v", err)
}
}