// - key stretching (SHA256-based HKDF)
// - key wrapping/unwrapping (Encrypt then MAC)
// - passphrase-based key derivation (Argon2id)
+// - descriptor computation (double SHA512)
package crypto
/*
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
- "fmt"
- "io"
+ "crypto/sha512"
+ "encoding/hex"
+ "errors"
"unsafe"
"golang.org/x/crypto/hkdf"
- "golang.org/x/sys/unix"
"fscrypt/metadata"
"fscrypt/util"
)
-// Lengths for our keys and buffers used for crypto.
-const (
- // We always use 256-bit keys internally (compared to 512-bit policy keys).
- InternalKeyLen = 32
- IVLen = 16
- SaltLen = 16
- // PolicyKeyLen is the length of all keys passed directly to the Keyring
- PolicyKeyLen = unix.FS_MAX_KEY_SIZE
+// Crypto error values
+var (
+ ErrBadAuth = errors.New("key authentication check failed")
+ ErrNegitiveLength = errors.New("negative length requested for key")
+ ErrKeyAlloc = util.SystemError("could not allocate memory for key")
+ ErrKeyFree = util.SystemError("could not free memory of key")
+ ErrKeyringLocate = util.SystemError("could not locate the session keyring")
+ ErrKeyringInsert = util.SystemError("could not insert key into the session keyring")
+ ErrRecoveryCode = errors.New("provided recovery code had incorrect format")
+ ErrLowEntropy = util.SystemError("insufficient entropy in pool to generate random bytes")
+ ErrRandNotSupported = util.SystemError("getrandom() not implemented; kernel must be v3.17 or later")
+ ErrRandFailed = util.SystemError("cannot get random bytes")
)
-// checkInputLength panics if "name" has invalid length (expected != actual)
-func checkInputLength(name string, expected, actual int) {
+// panicInputLength panics if "name" has invalid length (expected != actual)
+func panicInputLength(name string, expected, actual int) {
if expected != actual {
util.NeverError(util.InvalidLengthError(name, expected, actual))
}
}
+// checkWrappingKey returns an error if the wrapping key has the wrong length
+func checkWrappingKey(wrappingKey *Key) error {
+ l := wrappingKey.Len()
+ if l != metadata.InternalKeyLen {
+ return util.InvalidLengthError("wrapping key", metadata.InternalKeyLen, l)
+ }
+ return nil
+}
+
// stretchKey stretches a key of length KeyLen using unsalted HKDF to make two
// keys of length KeyLen.
func stretchKey(key *Key) (encKey, authKey *Key) {
- checkInputLength("hkdf key", InternalKeyLen, key.Len())
+ panicInputLength("hkdf key", metadata.InternalKeyLen, key.Len())
// The new hkdf function uses the hash and key to create a reader that
// can be used to securely initialize multiple keys. This means that
// also always have enough entropy to read two keys.
hkdf := hkdf.New(sha256.New, key.data, nil, nil)
- encKey, err := NewFixedLengthKeyFromReader(hkdf, InternalKeyLen)
+ encKey, err := NewFixedLengthKeyFromReader(hkdf, metadata.InternalKeyLen)
util.NeverError(err)
- authKey, err = NewFixedLengthKeyFromReader(hkdf, InternalKeyLen)
+ authKey, err = NewFixedLengthKeyFromReader(hkdf, metadata.InternalKeyLen)
util.NeverError(err)
return
// function can be used to either encrypt or decrypt input of any size. Note
// that input and output must be the same size.
func aesCTR(key *Key, iv, input, output []byte) {
- checkInputLength("aesCTR key", InternalKeyLen, key.Len())
- checkInputLength("aesCTR iv", IVLen, len(iv))
- checkInputLength("aesCTR output", len(input), len(output))
+ panicInputLength("aesCTR key", metadata.InternalKeyLen, key.Len())
+ panicInputLength("aesCTR iv", metadata.IVLen, len(iv))
+ panicInputLength("aesCTR output", len(input), len(output))
blockCipher, err := aes.NewCipher(key.data)
util.NeverError(err) // Key is checked to have correct length
// getHMAC returns the SHA256-based HMAC of some data using the provided key.
func getHMAC(key *Key, data ...[]byte) []byte {
- checkInputLength("hmac key", InternalKeyLen, key.Len())
+ panicInputLength("hmac key", metadata.InternalKeyLen, key.Len())
mac := hmac.New(sha256.New, key.data)
for _, buffer := range data {
// and an HMAC to verify the wrapping key was correct. All of this is included
// in the returned WrappedKeyData structure.
func Wrap(wrappingKey, secretKey *Key) (*metadata.WrappedKeyData, error) {
- if wrappingKey.Len() != InternalKeyLen {
- return nil, util.InvalidLengthError("wrapping key", InternalKeyLen, wrappingKey.Len())
+ err := checkWrappingKey(wrappingKey)
+ if err != nil {
+ return nil, err
}
- data := &metadata.WrappedKeyData{
- IV: make([]byte, IVLen),
- EncryptedKey: make([]byte, secretKey.Len()),
- }
+ data := &metadata.WrappedKeyData{EncryptedKey: make([]byte, secretKey.Len())}
// Get random IV
- if _, err := io.ReadFull(RandReader, data.IV); err != nil {
+ if data.IV, err = NewRandomBuffer(metadata.IVLen); err != nil {
return nil, err
}
// WrappedKeyData to get the unwrapped secret Key. The Wrapped Key data includes
// an authentication check, so an error will be returned if that check fails.
func Unwrap(wrappingKey *Key, data *metadata.WrappedKeyData) (*Key, error) {
- if wrappingKey.Len() != InternalKeyLen {
- return nil, util.InvalidLengthError("wrapping key", InternalKeyLen, wrappingKey.Len())
+ if err := checkWrappingKey(wrappingKey); err != nil {
+ return nil, err
}
// Stretch key for encryption and authentication (unsalted).
// Check validity of the HMAC
if !hmac.Equal(getHMAC(authKey, data.IV, data.EncryptedKey), data.Hmac) {
- return nil, fmt.Errorf("key authentication check failed")
+ return nil, ErrBadAuth
}
secretKey, err := newBlankKey(len(data.EncryptedKey))
return ctx
}
+// ComputeDescriptor computes the descriptor for a given cryptographic key. In
+// keeping with the process used in e4crypt, this uses the initial bytes
+// (formatted as hexadecimal) of the double application of SHA512 on the key.
+func ComputeDescriptor(key *Key) string {
+ h1 := sha512.Sum512(key.data)
+ h2 := sha512.Sum512(h1[:])
+ length := hex.DecodedLen(metadata.DescriptorLen)
+ return hex.EncodeToString(h2[:length])
+}
+
/*
PassphraseHash uses Argon2id to produce a Key given the passphrase, salt, and
hashing costs. This method is designed to take a long time and consume
attacks. For more info see: https://github.com/P-H-C/phc-winner-argon2
*/
func PassphraseHash(passphrase *Key, salt []byte, costs *metadata.HashingCosts) (*Key, error) {
- if len(salt) != SaltLen {
- return nil, util.InvalidLengthError("salt", SaltLen, len(salt))
+ if len(salt) != metadata.SaltLen {
+ return nil, util.InvalidLengthError("salt", metadata.SaltLen, len(salt))
}
// This key will hold the hashing output
- hash, err := newBlankKey(InternalKeyLen)
+ hash, err := newBlankKey(metadata.InternalKeyLen)
if err != nil {
return nil, err
}
if returnCode != C.ARGON2_OK {
hash.Wipe()
errorString := C.GoString(C.argon2_error_message(returnCode))
- return nil, util.SystemErrorF("argon2: %s", errorString)
+ return nil, util.SystemError("argon2: " + errorString)
}
return hash, nil
"crypto/sha256"
"encoding/hex"
"fmt"
+ "io"
"os"
"testing"
if err != nil {
t.Fatal(err)
}
+ defer key1.Wipe()
if !bytes.Equal(data, key1.data) {
t.Error("Key from reader contained incorrect data")
}
if err != nil {
t.Fatal(err)
}
+ defer key2.Wipe()
if !bytes.Equal([]byte("1234\n6"), key2.data) {
t.Error("Fixed length key from reader contained incorrect data")
}
// Making keys with negative length should fail
func TestInvalidLength(t *testing.T) {
- _, err := NewFixedLengthKeyFromReader(bytes.NewReader([]byte{1, 2, 3, 4}), -1)
+ key, err := NewFixedLengthKeyFromReader(ConstReader(1), -1)
if err == nil {
+ key.Wipe()
t.Error("Negative lengths should cause failure")
}
}
if err != nil {
t.Fatal(err)
}
+ defer key1.Wipe()
if key1.data != nil {
t.Error("FIxed length key from reader contained data")
}
if err != nil {
t.Fatal(err)
}
+ defer key2.Wipe()
if key2.data != nil {
t.Error("Key from empty reader contained data")
}
}
+// Test that enabling the disabling memory locking succeeds even if a key is
+// active when the variable changes.
+func TestEnableDisableMemoryLocking(t *testing.T) {
+ // Mlock on for creation, off for wiping
+ key, err := NewRandomKey(InternalKeyLen)
+ UseMlock = false
+ defer func() {
+ UseMlock = true
+ }()
+
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := key.Wipe(); err != nil {
+ t.Error(err)
+ }
+}
+
+// Test that disabling then enabling memory locking succeeds even if a key is
+// active when the variable changes.
+func TestDisableEnableMemoryLocking(t *testing.T) {
+ // Mlock off for creation, on for wiping
+ UseMlock = false
+ key2, err := NewRandomKey(InternalKeyLen)
+ UseMlock = true
+
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := key2.Wipe(); err != nil {
+ t.Error(err)
+ }
+}
+
// Test making keys long enough that the keys will have to resize
-func TestLongLength(t *testing.T) {
- // Key will have to resize 3 times
- data := bytes.Repeat([]byte{1}, os.Getpagesize()*5)
- key, err := NewKeyFromReader(bytes.NewReader(data))
+func TestKeyResize(t *testing.T) {
+ // Key will have to resize once
+ r := io.LimitReader(ConstReader(1), int64(os.Getpagesize())+1)
+ key, err := NewKeyFromReader(r)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer key.Wipe()
+ for i, b := range key.data {
+ if b != 1 {
+ t.Fatalf("Byte %d contained invalid data %q", i, b)
+ }
+ }
+}
+
+// Test making keys so long that many resizes are necessary
+func TestKeyLargeResize(t *testing.T) {
+ // Key will have to resize 7 times
+ r := io.LimitReader(ConstReader(1), int64(os.Getpagesize())*65)
+
+ // Turn off Mlocking as the key will exceed the limit on some systems.
+ UseMlock = false
+ key, err := NewKeyFromReader(r)
+ UseMlock = true
+
if err != nil {
t.Fatal(err)
}
- if !bytes.Equal(data, key.data) {
- t.Error("Key contained incorrect data")
+ defer key.Wipe()
+ for i, b := range key.data {
+ if b != 1 {
+ t.Fatalf("Byte %d contained invalid data %q", i, b)
+ }
}
}
}
encKey, authKey := stretchKey(fakeWrappingKey)
+ defer encKey.Wipe()
+ defer authKey.Wipe()
if !buffersDistinct(fakeWrappingKey.data, fakeValidPolicyKey.data,
encKey.data, authKey.data, data.IV, data.EncryptedKey, data.Hmac) {
if err != nil {
t.Fatal(err)
}
+ defer wk.Wipe()
for i := 0; i < 100; i++ {
sk, err := makeKey(2, i)
if err != nil {
// Attempts to Unwrap data with key after altering tweek, should fail
func testFailWithTweek(key *Key, data *WrappedKeyData, tweek []byte) error {
tweek[0]++
- _, err := Unwrap(key, data)
+ key, err := Unwrap(key, data)
+ if err == nil {
+ key.Wipe()
+ }
tweek[0]--
return err
}
if err != nil {
t.Fatal(err)
}
+ defer pk.Wipe()
+
costs := *hashTestCases[0].costs
costs.Time = 0
_, err = PassphraseHash(pk, fakeSalt, &costs)
if err != nil {
t.Fatal(err)
}
+ defer pk.Wipe()
+
costs := *hashTestCases[0].costs
costs.Memory = 7
_, err = PassphraseHash(pk, fakeSalt, &costs)
if err != nil {
t.Fatal(err)
}
+ defer pk.Wipe()
+
costs := *hashTestCases[0].costs
costs.Parallelism = 1 << 24
costs.Memory = 1 << 27 // Running n threads requires at least 8*n memory
}
func BenchmarkUnwrap(b *testing.B) {
+ b.StopTimer()
+
data, _ := Wrap(fakeWrappingKey, fakeValidPolicyKey)
+ b.StartTimer()
for n := 0; n < b.N; n++ {
- Unwrap(fakeWrappingKey, data)
+ key, err := Unwrap(fakeWrappingKey, data)
+ if err != nil {
+ b.Fatal(err)
+ }
+ key.Wipe()
}
}
func BenchmarkUnwrapNolock(b *testing.B) {
+ b.StopTimer()
+
UseMlock = false
defer func() {
UseMlock = true
}()
data, _ := Wrap(fakeWrappingKey, fakeValidPolicyKey)
+ b.StartTimer()
for n := 0; n < b.N; n++ {
- _, _ = Unwrap(fakeWrappingKey, data)
+ key, err := Unwrap(fakeWrappingKey, data)
+ if err != nil {
+ b.Fatal(err)
+ }
+ key.Wipe()
}
}
}
func benchmarkPassphraseHashing(b *testing.B, costs *HashingCosts) {
+ b.StopTimer()
+
+ pk, err := fakePassphraseKey()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer pk.Wipe()
+
+ b.StartTimer()
for n := 0; n < b.N; n++ {
- pk, err := fakePassphraseKey()
- if err != nil {
- b.Fatal(err)
- }
- defer pk.Wipe()
hash, err := PassphraseHash(pk, fakeSalt, costs)
hash.Wipe()
if err != nil {
import (
"bytes"
+ "crypto/subtle"
"encoding/base32"
- "fmt"
"io"
+ "log"
"os"
"runtime"
if length == 0 {
return &Key{data: nil}, nil
} else if length < 0 {
- return nil, util.InvalidInputF("requested key length %d is negative", length)
+ log.Printf("key length of %d is invalid", length)
+ return nil, ErrNegitiveLength
}
flags := keyMmapFlags
// See MAP_ANONYMOUS in http://man7.org/linux/man-pages/man2/mmap.2.html
data, err := unix.Mmap(-1, 0, length, keyProtection, flags)
if err != nil {
- return nil, util.SystemErrorF("could not mmap() buffer: %v", err)
+ log.Printf("unix.Mmap() with length=%d failed: %v", length, err)
+ return nil, ErrKeyAlloc
}
key := &Key{data: data}
}
if err := unix.Munmap(data); err != nil {
- return util.SystemErrorF("could not munmap() buffer: %v", err)
+ log.Printf("unix.Munmap() failed: %v", err)
+ return ErrKeyFree
}
}
return nil
return key.data
}
+// Equals compares the contents of two keys, returning true if they have the same
+// key data. This function runs in constant time.
+func (key *Key) Equals(key2 *Key) bool {
+ return subtle.ConstantTimeCompare(key.data, key2.data) == 1
+}
+
// resize returns a new key with size requestedSize and the appropriate data
// copied over. The original data is wiped. This method does nothing and returns
// itself if the key's length equals requestedSize.
}
// addPayloadToSessionKeyring adds the payload to the current session keyring as
-// type logon, returning the key's new ID.
-func addPayloadToSessionKeyring(payload []byte, description string) (int, error) {
+// type logon, returning an error on failure.
+func addPayloadToSessionKeyring(payload []byte, description string) error {
// We cannot add directly to KEY_SPEC_SESSION_KEYRING, as that will make
// a new session keyring if one does not exist, which will be garbage
// collected when the process terminates. Instead, we first get the ID
// keyring if a session keyring does not exist.
keyringID, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_SESSION_KEYRING, 0)
if err != nil {
- return 0, err
+ log.Printf("unix.KeyctlGetKeyringID failed: %v", err)
+ log.Print("could not get keyring ID of KEY_SPEC_SESSION_KEYRING")
+ return ErrKeyringLocate
}
- return unix.AddKey("logon", description, payload, keyringID)
+ if _, err = unix.AddKey("logon", description, payload, keyringID); err != nil {
+ log.Printf("unix.AddKey failed: %v", err)
+ log.Printf("could not insert %q into keyring (ID = %d)", description, keyringID)
+ return ErrKeyringInsert
+ }
+ return nil
}
// InsertPolicyKey puts the provided policy key into the kernel keyring with the
// provided descriptor, provided service prefix, and type logon. The key and
// descriptor must have the appropriate lengths.
func InsertPolicyKey(key *Key, descriptor string, service string) error {
- if key.Len() != PolicyKeyLen {
- return util.InvalidLengthError("Policy Key", PolicyKeyLen, key.Len())
+ if key.Len() != metadata.PolicyKeyLen {
+ return util.InvalidLengthError("Policy Key", metadata.PolicyKeyLen, key.Len())
}
if len(descriptor) != metadata.DescriptorLen {
fscryptKey := (*unix.FscryptKey)(util.Ptr(payload.data))
// Mode is ignored by the kernel
fscryptKey.Mode = 0
- fscryptKey.Size = PolicyKeyLen
+ fscryptKey.Size = metadata.PolicyKeyLen
copy(fscryptKey.Raw[:], key.data)
- if _, err := addPayloadToSessionKeyring(payload.data, service+descriptor); err != nil {
- return util.SystemErrorF("inserting key - %s: %v", descriptor, err)
+ if err := addPayloadToSessionKeyring(payload.data, service+descriptor); err != nil {
+ return err
}
return nil
encoding = base32.StdEncoding
blockSize = 8
separator = []byte("-")
- encodedLength = encoding.EncodedLen(InternalKeyLen)
+ encodedLength = encoding.EncodedLen(metadata.PolicyKeyLen)
decodedLength = encoding.DecodedLen(encodedLength)
// RecoveryCodeLength is the number of bytes in every recovery code
RecoveryCodeLength = (encodedLength/blockSize)*(blockSize+len(separator)) - len(separator)
// WARNING: This recovery key is enough to derive the original key, so it must
// be given the same level of protection as a raw cryptographic key.
func WriteRecoveryCode(key *Key, writer io.Writer) error {
- if key.Len() != InternalKeyLen {
- return util.InvalidLengthError("key", InternalKeyLen, key.Len())
+ if key.Len() != metadata.PolicyKeyLen {
+ return util.InvalidLengthError("key", metadata.PolicyKeyLen, key.Len())
}
// We store the base32 encoded data (without separators) in a temp key
for blockStart := blockSize; blockStart < encodedLength; blockStart += blockSize {
r.Read(inputSeparator)
if r.Err() == nil && !bytes.Equal(separator, inputSeparator) {
- return nil, fmt.Errorf("invalid separator: %q", inputSeparator)
+ log.Printf("separator of %q is invalid", inputSeparator)
+ return nil, ErrRecoveryCode
}
blockEnd := util.MinInt(blockStart+blockSize, encodedLength)
// If any reads have failed, return the error
if r.Err() != nil {
- return nil, r.Err()
+ log.Printf("error while reading recovery code: %v", r.Err())
+ return nil, ErrRecoveryCode
}
// Now we decode the key, resizing if necessary
}
if _, err = encoding.Decode(decodedKey.data, encodedKey.data); err != nil {
decodedKey.Wipe()
- return nil, err
+ log.Printf("error decoding recovery code: %v", err)
+ return nil, ErrRecoveryCode
}
- return decodedKey.resize(InternalKeyLen)
+ return decodedKey.resize(metadata.PolicyKeyLen)
}
import (
"io"
+ "log"
"golang.org/x/sys/unix"
-
- "fscrypt/util"
)
-/*
-RandReader uses the Linux Getrandom() syscall to read random bytes. If the
-operating system has insufficient randomness, the read will fail. This is an
-improvement over Go's built-in crypto/rand which will still return bytes if the
-system has insufficiency entropy (https://github.com/golang/go/issues/19274).
+// NewRandomBuffer uses the Linux Getrandom() syscall to create random bytes. If
+// the operating system has insufficient randomness, the buffer creation will
+// fail. This is an improvement over Go's built-in crypto/rand which will still
+// return bytes if the system has insufficiency entropy.
+// See: https://github.com/golang/go/issues/19274
+//
+// While this syscall was only introduced in Kernel v3.17, it predates the
+// introduction of filesystem encryption, so it introduces no additional
+// compatibility issues.
+func NewRandomBuffer(length int) ([]byte, error) {
+ buffer := make([]byte, length)
+ if _, err := io.ReadFull(randReader{}, buffer); err != nil {
+ return nil, err
+ }
+ return buffer, nil
+}
-While this syscall was only introduced in Kernel v3.17, it predates the
-introduction of filesystem encryption, so it introduces no additional
-compatibility issues.
-*/
-var RandReader io.Reader = randReader{}
+// NewRandomKey creates a random key of the specified length. This function uses
+// the same random number generation process a NewRandomBuffer.
+func NewRandomKey(length int) (*Key, error) {
+ return NewFixedLengthKeyFromReader(randReader{}, length)
+}
-// As we just call into Getrandom, no internal data is needed.
+// randReader just calls into Getrandom, so no internal data is needed.
type randReader struct{}
func (r randReader) Read(buffer []byte) (int, error) {
case nil:
return n, nil
case unix.EAGAIN:
- return 0, util.SystemErrorF("entropy pool not yet initialized")
+ return 0, ErrLowEntropy
case unix.ENOSYS:
- return 0, util.SystemErrorF("getrandom not implemented; kernel must be v3.17 or later")
+ return 0, ErrRandNotSupported
default:
- return 0, util.SystemErrorF("cannot get randomness: %v", err)
+ log.Printf("unix.Getrandom failed: %v", err)
+ return 0, ErrRandFailed
}
}
-
-// NewRandomKey creates a random key (from RandReader) of the specified length.
-func NewRandomKey(length int) (*Key, error) {
- return NewFixedLengthKeyFromReader(RandReader, length)
-}
import (
"bytes"
"fmt"
+ "fscrypt/metadata"
"testing"
)
-const fakeSecretRecoveryCode = "EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTA===="
+const fakeSecretRecoveryCode = "EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJRG-EYTCMJQ="
-var fakeSecretKey, _ = makeKey(38, InternalKeyLen)
+var fakeSecretKey, _ = makeKey(38, metadata.PolicyKeyLen)
// Note that this function is INSECURE. FOR TESTING ONLY
func getRecoveryCodeFromKey(key *Key) ([]byte, error) {
}
func getRandomRecoveryCodeBuffer() ([]byte, error) {
- key, err := NewRandomKey(InternalKeyLen)
+ key, err := NewRandomKey(metadata.PolicyKeyLen)
if err != nil {
return nil, err
}
+ defer key.Wipe()
return getRecoveryCodeFromKey(key)
}
if err != nil {
return err
}
+ defer key2.Wipe()
if !bytes.Equal(key.data, key2.data) {
return fmt.Errorf("encoding then decoding %x didn't yield the same key", key.data)
if err != nil {
return err
}
+ defer key.Wipe()
buf2, err := getRecoveryCodeFromKey(key)
if err != nil {
}
func TestEncodeDecode(t *testing.T) {
- key, err := NewRandomKey(InternalKeyLen)
+ key, err := NewRandomKey(metadata.PolicyKeyLen)
if err != nil {
t.Fatal(err)
}
+ defer key.Wipe()
if err = testKeyEncodeDecode(key); err != nil {
t.Error(err)
}
func TestWrongLengthError(t *testing.T) {
- key, err := NewRandomKey(InternalKeyLen - 1)
+ key, err := NewRandomKey(metadata.PolicyKeyLen - 1)
if err != nil {
t.Fatal(err)
}
+ defer key.Wipe()
if _, err = getRecoveryCodeFromKey(key); err == nil {
t.Error("key with wrong length should have failed to encode")
func TestBadCharacterError(t *testing.T) {
buf, err := getRandomRecoveryCodeBuffer()
+ if err != nil {
+ t.Fatal(err)
+ }
// Lowercase letters not allowed
buf[3] = 'k'
- if _, err = getKeyFromRecoveryCode(buf); err == nil {
+ if key, err := getKeyFromRecoveryCode(buf); err == nil {
+ key.Wipe()
t.Error("lowercase letters should make decoding fail")
}
}
func TestBadEndCharacterError(t *testing.T) {
buf, err := getRandomRecoveryCodeBuffer()
+ if err != nil {
+ t.Fatal(err)
+ }
// Separator must be '-'
buf[blockSize] = '_'
- if _, err = getKeyFromRecoveryCode(buf); err == nil {
+ if key, err := getKeyFromRecoveryCode(buf); err == nil {
+ key.Wipe()
t.Error("any separator that isn't '-' should make decoding fail")
}
}
func BenchmarkEncode(b *testing.B) {
- key, err := NewRandomKey(InternalKeyLen)
+ b.StopTimer()
+
+ key, err := NewRandomKey(metadata.PolicyKeyLen)
if err != nil {
b.Fatal(err)
}
+ defer key.Wipe()
+ b.StartTimer()
for n := 0; n < b.N; n++ {
if _, err = getRecoveryCodeFromKey(key); err != nil {
b.Fatal(err)
}
func BenchmarkDecode(b *testing.B) {
+ b.StopTimer()
+
buf, err := getRandomRecoveryCodeBuffer()
if err != nil {
b.Fatal(err)
}
+ b.StartTimer()
for n := 0; n < b.N; n++ {
- if _, err = getKeyFromRecoveryCode(buf); err != nil {
+ key, err := getKeyFromRecoveryCode(buf)
+ if err != nil {
b.Fatal(err)
}
+ key.Wipe()
}
}
func BenchmarkEncodeDecode(b *testing.B) {
- key, err := NewRandomKey(InternalKeyLen)
+ b.StopTimer()
+
+ key, err := NewRandomKey(metadata.PolicyKeyLen)
if err != nil {
b.Fatal(err)
}
+ defer key.Wipe()
+ b.StartTimer()
for n := 0; n < b.N; n++ {
if err = testKeyEncodeDecode(key); err != nil {
b.Fatal(err)
}
func BenchmarkDecodeEncode(b *testing.B) {
+ b.StopTimer()
+
buf, err := getRandomRecoveryCodeBuffer()
if err != nil {
b.Fatal(err)
}
+ b.StartTimer()
for n := 0; n < b.N; n++ {
if err = testRecoveryDecodeEncode(buf); err != nil {
b.Fatal(err)