mirror of
https://github.com/rclone/rclone.git
synced 2024-11-26 02:14:42 +01:00
crypt: ask for a second password for the salt
This commit is contained in:
parent
226c2a0d83
commit
663dd6ed8b
@ -47,7 +47,7 @@ var (
|
||||
ErrorBadSpreadResultTooShort = errors.New("bad unspread - result too short")
|
||||
ErrorBadSpreadDidntMatch = errors.New("bad unspread - directory prefix didn't match")
|
||||
ErrorFileClosed = errors.New("file already closed")
|
||||
scryptSalt = []byte{0xA8, 0x0D, 0xF4, 0x3A, 0x8F, 0xBD, 0x03, 0x08, 0xA7, 0xCA, 0xB8, 0x3E, 0x58, 0x1F, 0x86, 0xB1}
|
||||
defaultSalt = []byte{0xA8, 0x0D, 0xF4, 0x3A, 0x8F, 0xBD, 0x03, 0x08, 0xA7, 0xCA, 0xB8, 0x3E, 0x58, 0x1F, 0x86, 0xB1}
|
||||
)
|
||||
|
||||
// Global variables
|
||||
@ -81,7 +81,8 @@ type cipher struct {
|
||||
cryptoRand io.Reader // read crypto random numbers from here
|
||||
}
|
||||
|
||||
func newCipher(flatten int, password string) (*cipher, error) {
|
||||
// newCipher initialises the cipher. If salt is "" then it uses a built in salt val
|
||||
func newCipher(flatten int, password, salt string) (*cipher, error) {
|
||||
c := &cipher{
|
||||
flatten: flatten,
|
||||
cryptoRand: rand.Reader,
|
||||
@ -89,7 +90,7 @@ func newCipher(flatten int, password string) (*cipher, error) {
|
||||
c.buffers.New = func() interface{} {
|
||||
return make([]byte, blockSize)
|
||||
}
|
||||
err := c.Key(password)
|
||||
err := c.Key(password, salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -97,18 +98,24 @@ func newCipher(flatten int, password string) (*cipher, error) {
|
||||
}
|
||||
|
||||
// Key creates all the internal keys from the password passed in using
|
||||
// scrypt. We use a fixed salt just to make attackers lives slighty
|
||||
// harder than using no salt.
|
||||
// scrypt.
|
||||
//
|
||||
// If salt is "" we use a fixed salt just to make attackers lives
|
||||
// slighty harder than using no salt.
|
||||
//
|
||||
// Note that empty passsword makes all 0x00 keys which is used in the
|
||||
// tests.
|
||||
func (c *cipher) Key(password string) (err error) {
|
||||
func (c *cipher) Key(password, salt string) (err error) {
|
||||
const keySize = len(c.dataKey) + len(c.nameKey) + len(c.nameTweak)
|
||||
var saltBytes = defaultSalt
|
||||
if salt != "" {
|
||||
saltBytes = []byte(salt)
|
||||
}
|
||||
var key []byte
|
||||
if password == "" {
|
||||
key = make([]byte, keySize)
|
||||
} else {
|
||||
key, err = scrypt.Key([]byte(password), scryptSalt, 16384, 8, 1, keySize)
|
||||
key, err = scrypt.Key([]byte(password), saltBytes, 16384, 8, 1, keySize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ func TestDecodeFileName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEncryptSegment(t *testing.T) {
|
||||
c, _ := newCipher(0, "")
|
||||
c, _ := newCipher(0, "", "")
|
||||
for _, test := range []struct {
|
||||
in string
|
||||
expected string
|
||||
@ -166,7 +166,7 @@ func TestEncryptSegment(t *testing.T) {
|
||||
|
||||
func TestDecryptSegment(t *testing.T) {
|
||||
// We've tested the forwards above, now concentrate on the errors
|
||||
c, _ := newCipher(0, "")
|
||||
c, _ := newCipher(0, "", "")
|
||||
for _, test := range []struct {
|
||||
in string
|
||||
expectedErr error
|
||||
@ -229,12 +229,12 @@ func TestUnspreadName(t *testing.T) {
|
||||
|
||||
func TestEncryptName(t *testing.T) {
|
||||
// First no flatten
|
||||
c, _ := newCipher(0, "")
|
||||
c, _ := newCipher(0, "", "")
|
||||
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s", c.EncryptName("1"))
|
||||
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng", c.EncryptName("1/12"))
|
||||
assert.Equal(t, "p0e52nreeaj0a5ea7s64m4j72s/l42g6771hnv3an9cgc8cr2n1ng/qgm4avr35m5loi1th53ato71v0", c.EncryptName("1/12/123"))
|
||||
// Now with flatten
|
||||
c, _ = newCipher(3, "")
|
||||
c, _ = newCipher(3, "", "")
|
||||
assert.Equal(t, "k/g/t/kgtickdcigo7600huebjl3ubu4", c.EncryptName("1/12/123"))
|
||||
}
|
||||
|
||||
@ -255,7 +255,7 @@ func TestDecryptName(t *testing.T) {
|
||||
{1, "k/g/t/i/kgtickdcigo7600huebjl3ubu4", "1/12/123", nil},
|
||||
{1, "k/x/t/i/kgtickdcigo7600huebjl3ubu4", "", ErrorBadSpreadDidntMatch},
|
||||
} {
|
||||
c, _ := newCipher(test.flatten, "")
|
||||
c, _ := newCipher(test.flatten, "", "")
|
||||
actual, actualErr := c.DecryptName(test.in)
|
||||
what := fmt.Sprintf("Testing %q (flatten=%d)", test.in, test.flatten)
|
||||
assert.Equal(t, test.expected, actual, what)
|
||||
@ -264,7 +264,7 @@ func TestDecryptName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEncryptedSize(t *testing.T) {
|
||||
c, _ := newCipher(0, "")
|
||||
c, _ := newCipher(0, "", "")
|
||||
for _, test := range []struct {
|
||||
in int64
|
||||
expected int64
|
||||
@ -288,7 +288,7 @@ func TestEncryptedSize(t *testing.T) {
|
||||
|
||||
func TestDecryptedSize(t *testing.T) {
|
||||
// Test the errors since we tested the reverse above
|
||||
c, _ := newCipher(0, "")
|
||||
c, _ := newCipher(0, "", "")
|
||||
for _, test := range []struct {
|
||||
in int64
|
||||
expectedErr error
|
||||
@ -521,7 +521,7 @@ func (z *zeroes) Read(p []byte) (n int, err error) {
|
||||
|
||||
// Test encrypt decrypt with different buffer sizes
|
||||
func testEncryptDecrypt(t *testing.T, bufSize int, copySize int64) {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
c.cryptoRand = &zeroes{} // zero out the nonce
|
||||
buf := make([]byte, bufSize)
|
||||
@ -591,7 +591,7 @@ func TestEncryptData(t *testing.T) {
|
||||
{[]byte{1}, file1},
|
||||
{[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, file16},
|
||||
} {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator
|
||||
|
||||
@ -614,7 +614,7 @@ func TestEncryptData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewEncrypter(t *testing.T) {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator
|
||||
|
||||
@ -658,7 +658,7 @@ func (c *closeDetector) Close() error {
|
||||
}
|
||||
|
||||
func TestNewDecrypter(t *testing.T) {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
c.cryptoRand = newRandomSource(1E8) // nodge the crypto rand generator
|
||||
|
||||
@ -700,7 +700,7 @@ func TestNewDecrypter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecrypterRead(t *testing.T) {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test truncating the header
|
||||
@ -744,7 +744,7 @@ func TestDecrypterRead(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecrypterClose(t *testing.T) {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
cd := newCloseDetector(bytes.NewBuffer(file16))
|
||||
@ -780,7 +780,7 @@ func TestDecrypterClose(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutGetBlock(t *testing.T) {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
block := c.getBlock()
|
||||
@ -791,7 +791,7 @@ func TestPutGetBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKey(t *testing.T) {
|
||||
c, err := newCipher(0, "")
|
||||
c, err := newCipher(0, "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check zero keys OK
|
||||
@ -799,17 +799,27 @@ func TestKey(t *testing.T) {
|
||||
assert.Equal(t, [32]byte{}, c.nameKey)
|
||||
assert.Equal(t, [16]byte{}, c.nameTweak)
|
||||
|
||||
require.NoError(t, c.Key("potato"))
|
||||
require.NoError(t, c.Key("potato", ""))
|
||||
assert.Equal(t, [32]byte{0x74, 0x55, 0xC7, 0x1A, 0xB1, 0x7C, 0x86, 0x5B, 0x84, 0x71, 0xF4, 0x7B, 0x79, 0xAC, 0xB0, 0x7E, 0xB3, 0x1D, 0x56, 0x78, 0xB8, 0x0C, 0x7E, 0x2E, 0xAF, 0x4F, 0xC8, 0x06, 0x6A, 0x9E, 0xE4, 0x68}, c.dataKey)
|
||||
assert.Equal(t, [32]byte{0x76, 0x5D, 0xA2, 0x7A, 0xB1, 0x5D, 0x77, 0xF9, 0x57, 0x96, 0x71, 0x1F, 0x7B, 0x93, 0xAD, 0x63, 0xBB, 0xB4, 0x84, 0x07, 0x2E, 0x71, 0x80, 0xA8, 0xD1, 0x7A, 0x9B, 0xBE, 0xC1, 0x42, 0x70, 0xD0}, c.nameKey)
|
||||
assert.Equal(t, [16]byte{0xC1, 0x8D, 0x59, 0x32, 0xF5, 0x5B, 0x28, 0x28, 0xC5, 0xE1, 0xE8, 0x72, 0x15, 0x52, 0x03, 0x10}, c.nameTweak)
|
||||
|
||||
require.NoError(t, c.Key("Potato"))
|
||||
require.NoError(t, c.Key("Potato", ""))
|
||||
assert.Equal(t, [32]byte{0xAE, 0xEA, 0x6A, 0xD3, 0x47, 0xDF, 0x75, 0xB9, 0x63, 0xCE, 0x12, 0xF5, 0x76, 0x23, 0xE9, 0x46, 0xD4, 0x2E, 0xD8, 0xBF, 0x3E, 0x92, 0x8B, 0x39, 0x24, 0x37, 0x94, 0x13, 0x3E, 0x5E, 0xF7, 0x5E}, c.dataKey)
|
||||
assert.Equal(t, [32]byte{0x54, 0xF7, 0x02, 0x6E, 0x8A, 0xFC, 0x56, 0x0A, 0x86, 0x63, 0x6A, 0xAB, 0x2C, 0x9C, 0x51, 0x62, 0xE5, 0x1A, 0x12, 0x23, 0x51, 0x83, 0x6E, 0xAF, 0x50, 0x42, 0x0F, 0x98, 0x1C, 0x86, 0x0A, 0x19}, c.nameKey)
|
||||
assert.Equal(t, [16]byte{0xF8, 0xC1, 0xB6, 0x27, 0x2D, 0x52, 0x9B, 0x4A, 0x8F, 0xDA, 0xEB, 0x42, 0x4A, 0x28, 0xDD, 0xF3}, c.nameTweak)
|
||||
|
||||
require.NoError(t, c.Key(""))
|
||||
require.NoError(t, c.Key("potato", "sausage"))
|
||||
assert.Equal(t, [32]uint8{0x8e, 0x9b, 0x6b, 0x99, 0xf8, 0x69, 0x4, 0x67, 0xa0, 0x71, 0xf9, 0xcb, 0x92, 0xd0, 0xaa, 0x78, 0x7f, 0x8f, 0xf1, 0x78, 0xbe, 0xc9, 0x6f, 0x99, 0x9f, 0xd5, 0x20, 0x6e, 0x64, 0x4a, 0x1b, 0x50}, c.dataKey)
|
||||
assert.Equal(t, [32]uint8{0x3e, 0xa9, 0x5e, 0xf6, 0x81, 0x78, 0x2d, 0xc9, 0xd9, 0x95, 0x5d, 0x22, 0x5b, 0xfd, 0x44, 0x2c, 0x6f, 0x5d, 0x68, 0x97, 0xb0, 0x29, 0x1, 0x5c, 0x6f, 0x46, 0x2e, 0x2a, 0x9d, 0xae, 0x2c, 0xe3}, c.nameKey)
|
||||
assert.Equal(t, [16]uint8{0xf1, 0x7f, 0xd7, 0x14, 0x1d, 0x65, 0x27, 0x4f, 0x36, 0x3f, 0xc2, 0xa0, 0x4d, 0xd2, 0x14, 0x8a}, c.nameTweak)
|
||||
|
||||
require.NoError(t, c.Key("potato", "Sausage"))
|
||||
assert.Equal(t, [32]uint8{0xda, 0x81, 0x8c, 0x67, 0xef, 0x11, 0xf, 0xc8, 0xd5, 0xc8, 0x62, 0x4b, 0x7f, 0xe2, 0x9e, 0x35, 0x35, 0xb0, 0x8d, 0x79, 0x84, 0x89, 0xac, 0xcb, 0xa0, 0xff, 0x2, 0x72, 0x3, 0x1a, 0x5e, 0x64}, c.dataKey)
|
||||
assert.Equal(t, [32]uint8{0x2, 0x81, 0x7e, 0x7b, 0xea, 0x99, 0x81, 0x5a, 0xd0, 0x2d, 0xb9, 0x64, 0x48, 0xb0, 0x28, 0x27, 0x7c, 0x20, 0xb4, 0xd4, 0xa4, 0x68, 0xad, 0x4e, 0x5c, 0x29, 0xf, 0x79, 0xef, 0xee, 0xdb, 0x3b}, c.nameKey)
|
||||
assert.Equal(t, [16]uint8{0x9a, 0xb5, 0xb, 0x3d, 0xcb, 0x60, 0x59, 0x55, 0xa5, 0x4d, 0xe6, 0xb6, 0x47, 0x3, 0x23, 0xe2}, c.nameTweak)
|
||||
|
||||
require.NoError(t, c.Key("", ""))
|
||||
assert.Equal(t, [32]byte{}, c.dataKey)
|
||||
assert.Equal(t, [32]byte{}, c.nameKey)
|
||||
assert.Equal(t, [16]byte{}, c.nameTweak)
|
||||
|
@ -49,6 +49,11 @@ func init() {
|
||||
Name: "password",
|
||||
Help: "Password or pass phrase for encryption.",
|
||||
IsPassword: true,
|
||||
}, {
|
||||
Name: "password2",
|
||||
Help: "Password or pass phrase for salt. Optional but recommended.\nShould be different to the previous password.",
|
||||
IsPassword: true,
|
||||
Optional: true,
|
||||
}},
|
||||
})
|
||||
}
|
||||
@ -64,7 +69,14 @@ func NewFs(name, rpath string) (fs.Fs, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decrypt password")
|
||||
}
|
||||
cipher, err := newCipher(flatten, password)
|
||||
salt := fs.ConfigFile.MustValue(name, "password2", "")
|
||||
if salt != "" {
|
||||
salt, err = fs.Reveal(salt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decrypt password2")
|
||||
}
|
||||
}
|
||||
cipher, err := newCipher(flatten, password, salt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to make cipher")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user