diff --git a/amazonclouddrive/amazonclouddrive.go b/amazonclouddrive/amazonclouddrive.go index 0d012c34e..12bf3942b 100644 --- a/amazonclouddrive/amazonclouddrive.go +++ b/amazonclouddrive/amazonclouddrive.go @@ -34,7 +34,7 @@ import ( const ( rcloneClientID = "amzn1.application-oa2-client.6bf18d2d1f5b485c94c8988bb03ad0e7" - rcloneEncryptedClientSecret = "k8/NyszKm5vEkZXAwsbGkd6C3NrbjIqMg4qEhIeF14Szub2wur+/teS3ubXgsLe9//+tr/qoqK+lq6mg8vWkoA==" + rcloneEncryptedClientSecret = "ZP12wYlGw198FtmqfOxyNAGXU3fwVcQdmt--ba1d00wJnUs0LOzvVyXVDbqhbcUqnr5Vd1QejwWmiv1Ep7UJG1kUQeuBP5n9goXWd5MrAf0" folderKind = "FOLDER" fileKind = "FILE" assetKind = "ASSET" @@ -57,7 +57,7 @@ var ( TokenURL: "https://api.amazon.com/auth/o2/token", }, ClientID: rcloneClientID, - ClientSecret: fs.Reveal(rcloneEncryptedClientSecret), + ClientSecret: fs.MustReveal(rcloneEncryptedClientSecret), RedirectURL: oauthutil.RedirectURL, } ) diff --git a/drive/drive.go b/drive/drive.go index a33d23af5..542950bf7 100644 --- a/drive/drive.go +++ b/drive/drive.go @@ -30,7 +30,7 @@ import ( // Constants const ( rcloneClientID = "202264815644.apps.googleusercontent.com" - rcloneEncryptedClientSecret = "8p/yms3OlNXE9OTDl/HLypf9gdiJ5cT3" + rcloneEncryptedClientSecret = "eX8GpZTVx3vxMWVkuuBdDWmAUE6rGhTwVrvG9GhllYccSdj2-mvHVg" driveFolderType = "application/vnd.google-apps.folder" timeFormatIn = time.RFC3339 timeFormatOut = "2006-01-02T15:04:05.000000000Z07:00" @@ -57,7 +57,7 @@ var ( Scopes: []string{"https://www.googleapis.com/auth/drive"}, Endpoint: google.Endpoint, ClientID: rcloneClientID, - ClientSecret: fs.Reveal(rcloneEncryptedClientSecret), + ClientSecret: fs.MustReveal(rcloneEncryptedClientSecret), RedirectURL: oauthutil.TitleBarRedirectURL, } mimeTypeToExtension = map[string]string{ diff --git a/dropbox/dropbox.go b/dropbox/dropbox.go index 76ae9239b..7c6231823 100644 --- a/dropbox/dropbox.go +++ b/dropbox/dropbox.go @@ -29,7 +29,7 @@ import ( // Constants const ( rcloneAppKey = "5jcck7diasz0rqy" - rcloneEncryptedAppSecret = "m8WRxJ6b1Z/Y25fDwJWS" + rcloneEncryptedAppSecret = "fRS5vVLr2v6FbyXYnIgjwBuUAt0osq_QZTXAEcmZ7g" metadataLimit = dropbox.MetadataLimitDefault // max items to fetch at once ) @@ -139,7 +139,7 @@ func newDropbox(name string) (*dropbox.Dropbox, error) { } appSecret := fs.ConfigFile.MustValue(name, "app_secret") if appSecret == "" { - appSecret = fs.Reveal(rcloneEncryptedAppSecret) + appSecret = fs.MustReveal(rcloneEncryptedAppSecret) } err := db.SetAppInfo(appKey, appSecret) diff --git a/fs/config.go b/fs/config.go index 2f4902cfb..738820551 100644 --- a/fs/config.go +++ b/fs/config.go @@ -5,6 +5,8 @@ package fs import ( "bufio" "bytes" + "crypto/aes" + "crypto/cipher" "crypto/rand" "crypto/sha256" "crypto/tls" @@ -191,25 +193,85 @@ func (x *SizeSuffix) Type() string { // Check it satisfies the interface var _ pflag.Value = (*SizeSuffix)(nil) -// Obscure a config value -func Obscure(x string) string { - y := []byte(x) - for i := range y { - y[i] ^= byte(i) ^ 0xAA +// crypt internals +var ( + cryptKey = []byte{ + 0x9c, 0x93, 0x5b, 0x48, 0x73, 0x0a, 0x55, 0x4d, + 0x6b, 0xfd, 0x7c, 0x63, 0xc8, 0x86, 0xa9, 0x2b, + 0xd3, 0x90, 0x19, 0x8e, 0xb8, 0x12, 0x8a, 0xfb, + 0xf4, 0xde, 0x16, 0x2b, 0x8b, 0x95, 0xf6, 0x38, } - return base64.StdEncoding.EncodeToString(y) + cryptBlock cipher.Block + cryptRand = rand.Reader +) + +// crypt transforms in to out using iv under AES-CTR. +// +// in and out may be the same buffer. +// +// Note encryption and decryption are the same operation +func crypt(out, in, iv []byte) error { + if cryptBlock == nil { + var err error + cryptBlock, err = aes.NewCipher(cryptKey) + if err != nil { + return err + } + } + stream := cipher.NewCTR(cryptBlock, iv) + stream.XORKeyStream(out, in) + return nil } -// Reveal a config value -func Reveal(y string) string { - x, err := base64.StdEncoding.DecodeString(y) +// Obscure a value +// +// This is done by encrypting with AES-CTR +func Obscure(x string) (string, error) { + plaintext := []byte(x) + ciphertext := make([]byte, aes.BlockSize+len(plaintext)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(cryptRand, iv); err != nil { + return "", errors.Wrap(err, "failed to read iv") + } + if err := crypt(ciphertext[aes.BlockSize:], plaintext, iv); err != nil { + return "", errors.Wrap(err, "encrypt failed") + } + return base64.RawURLEncoding.EncodeToString(ciphertext), nil +} + +// MustObscure obscures a value, exiting with a fatal error if it failed +func MustObscure(x string) string { + out, err := Obscure(x) if err != nil { - log.Fatalf("Failed to reveal %q: %v", y, err) + log.Fatalf("Obscure failed: %v", err) } - for i := range x { - x[i] ^= byte(i) ^ 0xAA + return out +} + +// Reveal an obscured value +func Reveal(x string) (string, error) { + ciphertext, err := base64.RawURLEncoding.DecodeString(x) + if err != nil { + return "", errors.Wrap(err, "base64 decode failed") } - return string(x) + if len(ciphertext) < aes.BlockSize { + return "", errors.New("input too short") + } + buf := ciphertext[aes.BlockSize:] + iv := ciphertext[:aes.BlockSize] + if err := crypt(buf, buf, iv); err != nil { + return "", errors.Wrap(err, "decrypt failed") + } + return string(buf), nil +} + +// MustReveal reveals an obscured value, exiting with a fatal error if it failed +func MustReveal(x string) string { + out, err := Reveal(x) + if err != nil { + log.Fatalf("Reveal failed: %v", err) + } + return out } // ConfigInfo is filesystem config options diff --git a/fs/config_test.go b/fs/config_test.go index a9803dd54..868259907 100644 --- a/fs/config_test.go +++ b/fs/config_test.go @@ -1,6 +1,8 @@ package fs import ( + "bytes" + "crypto/rand" "testing" "github.com/stretchr/testify/assert" @@ -85,17 +87,48 @@ func TestSizeSuffixSet(t *testing.T) { } } -func TestReveal(t *testing.T) { +func TestObscure(t *testing.T) { for _, test := range []struct { in string want string + iv string }{ - {"", ""}, - {"2sTcyNrA", "potato"}, + {"", "YWFhYWFhYWFhYWFhYWFhYQ", "aaaaaaaaaaaaaaaa"}, + {"potato", "YWFhYWFhYWFhYWFhYWFhYXMaGgIlEQ", "aaaaaaaaaaaaaaaa"}, + {"potato", "YmJiYmJiYmJiYmJiYmJiYp3gcEWbAw", "bbbbbbbbbbbbbbbb"}, } { - got := Reveal(test.in) + cryptRand = bytes.NewBufferString(test.iv) + got, err := Obscure(test.in) + cryptRand = rand.Reader + assert.NoError(t, err) assert.Equal(t, test.want, got) - assert.Equal(t, test.in, Obscure(got), "not bidirectional") + recoveredIn, err := Reveal(got) + assert.NoError(t, err) + assert.Equal(t, test.in, recoveredIn, "not bidirectional") + // Now the Must variants + cryptRand = bytes.NewBufferString(test.iv) + got = MustObscure(test.in) + cryptRand = rand.Reader + assert.Equal(t, test.want, got) + recoveredIn = MustReveal(got) + assert.Equal(t, test.in, recoveredIn, "not bidirectional") + + } +} + +// Test some error cases +func TestReveal(t *testing.T) { + for _, test := range []struct { + in string + wantErr string + }{ + {"YmJiYmJiYmJiYmJiYmJiYp*gcEWbAw", "base64 decode failed: illegal base64 data at input byte 22"}, + {"aGVsbG8", "input too short"}, + {"", "input too short"}, + } { + gotString, gotErr := Reveal(test.in) + assert.Equal(t, "", gotString) + assert.Equal(t, test.wantErr, gotErr.Error()) } } diff --git a/googlecloudstorage/googlecloudstorage.go b/googlecloudstorage/googlecloudstorage.go index 6c8fecd6d..d3cf44981 100644 --- a/googlecloudstorage/googlecloudstorage.go +++ b/googlecloudstorage/googlecloudstorage.go @@ -37,7 +37,7 @@ import ( const ( rcloneClientID = "202264815644.apps.googleusercontent.com" - rcloneEncryptedClientSecret = "8p/yms3OlNXE9OTDl/HLypf9gdiJ5cT3" + rcloneEncryptedClientSecret = "Uj7C9jGfb9gmeaV70Lh058cNkWvepr-Es9sBm0zdgil7JaOWF1VySw" timeFormatIn = time.RFC3339 timeFormatOut = "2006-01-02T15:04:05.000000000Z07:00" metaMtime = "mtime" // key to store mtime under in metadata @@ -50,7 +50,7 @@ var ( Scopes: []string{storage.DevstorageFullControlScope}, Endpoint: google.Endpoint, ClientID: rcloneClientID, - ClientSecret: fs.Reveal(rcloneEncryptedClientSecret), + ClientSecret: fs.MustReveal(rcloneEncryptedClientSecret), RedirectURL: oauthutil.TitleBarRedirectURL, } ) diff --git a/hubic/hubic.go b/hubic/hubic.go index b67300076..b62aada1e 100644 --- a/hubic/hubic.go +++ b/hubic/hubic.go @@ -23,7 +23,7 @@ import ( const ( rcloneClientID = "api_hubic_svWP970PvSWbw5G3PzrAqZ6X2uHeZBPI" - rcloneEncryptedClientSecret = "8MrG3pjWyJya4OnO9ZTS4emI+9fa1ouPgvfD2MbTzfDYvO/H5czFxsTXtcji4/Hz3snz8/CrzMzlxvP9//Ty/Q==" + rcloneEncryptedClientSecret = "leZKCcqy9movLhDWLVXX8cSLp_FzoiAPeEJOIOMRw1A5RuC4iLEPDYPWVF46adC_MVonnLdVEOTHVstfBOZ_lY4WNp8CK_YWlpRZ9diT5YI" ) // Globals @@ -38,7 +38,7 @@ var ( TokenURL: "https://api.hubic.com/oauth/token/", }, ClientID: rcloneClientID, - ClientSecret: fs.Reveal(rcloneEncryptedClientSecret), + ClientSecret: fs.MustReveal(rcloneEncryptedClientSecret), RedirectURL: oauthutil.RedirectLocalhostURL, } ) diff --git a/onedrive/onedrive.go b/onedrive/onedrive.go index adc27a6a5..ec8bf308c 100644 --- a/onedrive/onedrive.go +++ b/onedrive/onedrive.go @@ -26,7 +26,7 @@ import ( const ( rcloneClientID = "0000000044165769" - rcloneEncryptedClientSecret = "0+be4+jYw+7018HY6P3t/Izo+pTc+Yvt8+fy8NHU094=" + rcloneEncryptedClientSecret = "ugVWLNhKkVT1-cbTRO-6z1MlzwdW6aMwpKgNaFG-qXjEn_WfDnG9TVyRA5yuoliU" minSleep = 10 * time.Millisecond maxSleep = 2 * time.Second decayConstant = 2 // bigger for slower decay, exponential @@ -47,7 +47,7 @@ var ( TokenURL: "https://login.live.com/oauth20_token.srf", }, ClientID: rcloneClientID, - ClientSecret: fs.Reveal(rcloneEncryptedClientSecret), + ClientSecret: fs.MustReveal(rcloneEncryptedClientSecret), RedirectURL: oauthutil.RedirectPublicURL, } chunkSize = fs.SizeSuffix(10 * 1024 * 1024) diff --git a/yandex/yandex.go b/yandex/yandex.go index 762c31ea2..93c46aa1d 100644 --- a/yandex/yandex.go +++ b/yandex/yandex.go @@ -23,7 +23,7 @@ import ( //oAuth const ( rcloneClientID = "ac39b43b9eba4cae8ffb788c06d816a8" - rcloneEncryptedClientSecret = "k8jKzZnMmM+Wx5jAksPAwYKPgImOiN+FhNKD09KBg9A=" + rcloneEncryptedClientSecret = "EfyyNZ3YUEwXM5yAhi72G9YwKn2mkFrYwJNS7cY0TJAhFlX9K-uJFbGlpO-RYjrJ" ) // Globals @@ -35,7 +35,7 @@ var ( TokenURL: "https://oauth.yandex.com/token", //same as https://oauth.yandex.ru/token }, ClientID: rcloneClientID, - ClientSecret: fs.Reveal(rcloneEncryptedClientSecret), + ClientSecret: fs.MustReveal(rcloneEncryptedClientSecret), RedirectURL: oauthutil.RedirectURL, } )