2016-07-25 18:18:56 +00:00
// Package crypt provides wrappers for Fs and Object which implement encryption
package crypt
import (
2019-06-17 08:34:30 +00:00
"context"
2016-07-25 18:18:56 +00:00
"fmt"
"io"
2020-01-21 21:32:34 +00:00
"path"
2016-12-19 14:09:59 +00:00
"strings"
2017-10-16 19:52:12 +00:00
"time"
2016-07-25 18:18:56 +00:00
"github.com/pkg/errors"
2019-07-28 17:47:38 +00:00
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/accounting"
"github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/fs/config/configstruct"
"github.com/rclone/rclone/fs/config/obscure"
"github.com/rclone/rclone/fs/fspath"
"github.com/rclone/rclone/fs/hash"
2016-07-25 18:18:56 +00:00
)
2017-01-29 10:12:52 +00:00
// Globals
2016-07-25 18:18:56 +00:00
// Register with Fs
func init ( ) {
fs . Register ( & fs . RegInfo {
Name : "crypt" ,
Description : "Encrypt/Decrypt a remote" ,
NewFs : NewFs ,
2020-04-29 17:54:16 +00:00
CommandHelp : commandHelp ,
2016-07-25 18:18:56 +00:00
Options : [ ] fs . Option { {
2018-05-14 17:06:57 +00:00
Name : "remote" ,
Help : "Remote to encrypt/decrypt.\nNormally should contain a ':' and a path, eg \"myremote:path/to/dir\",\n\"myremote:bucket\" or maybe \"myremote:\" (not recommended)." ,
Required : true ,
2016-07-25 18:18:56 +00:00
} , {
2018-05-14 17:06:57 +00:00
Name : "filename_encryption" ,
Help : "How to encrypt the filenames." ,
Default : "standard" ,
2016-07-25 18:18:56 +00:00
Examples : [ ] fs . OptionExample {
{
2016-08-20 17:46:10 +00:00
Value : "standard" ,
Help : "Encrypt the filenames see the docs for the details." ,
2017-03-12 18:14:36 +00:00
} , {
Value : "obfuscate" ,
Help : "Very simple filename obfuscation." ,
2020-01-12 14:23:35 +00:00
} , {
Value : "off" ,
Help : "Don't encrypt the file names. Adds a \".bin\" extension only." ,
2016-07-25 18:18:56 +00:00
} ,
} ,
2017-11-06 07:35:53 +00:00
} , {
2020-02-28 11:14:05 +00:00
Name : "directory_name_encryption" ,
Help : ` Option to either encrypt directory names or leave them intact .
NB If filename_encryption is "off" then this option will do nothing . ` ,
2018-05-14 17:06:57 +00:00
Default : true ,
2017-11-06 07:35:53 +00:00
Examples : [ ] fs . OptionExample {
{
Value : "true" ,
Help : "Encrypt directory names." ,
} ,
{
Value : "false" ,
Help : "Don't encrypt directory names, leave them intact." ,
} ,
} ,
2016-07-25 18:18:56 +00:00
} , {
Name : "password" ,
Help : "Password or pass phrase for encryption." ,
IsPassword : true ,
2019-11-05 11:53:44 +00:00
Required : true ,
2016-08-19 19:02:02 +00:00
} , {
Name : "password2" ,
Help : "Password or pass phrase for salt. Optional but recommended.\nShould be different to the previous password." ,
IsPassword : true ,
2020-06-27 10:40:15 +00:00
} , {
Name : "server_side_across_configs" ,
Default : false ,
Help : ` Allow server side operations ( eg copy ) to work across different crypt configs .
Normally this option is not what you want , but if you have two crypts
pointing to the same backend you can use it .
This can be used , for example , to change file name encryption type
without re - uploading all the data . Just make two crypt backends
pointing to two different directories with the single changed
parameter and use rclone move to move the files between the crypt
remotes . ` ,
Advanced : true ,
2018-05-14 17:06:57 +00:00
} , {
2018-10-01 17:36:15 +00:00
Name : "show_mapping" ,
Help : ` For all files listed show how the names encrypt .
If this flag is set then for each file that the remote is asked to
list , it will log ( at level INFO ) a line stating the decrypted file
name and the encrypted file name .
This is so you can work out which encrypted names are which decrypted
names just in case you need to do something with the encrypted file
names , or for debugging purposes . ` ,
2018-05-14 17:06:57 +00:00
Default : false ,
Hide : fs . OptionHideConfigurator ,
Advanced : true ,
2016-07-25 18:18:56 +00:00
} } ,
} )
}
2018-05-14 17:06:57 +00:00
// newCipherForConfig constructs a Cipher for the given config name
2020-04-09 15:17:17 +00:00
func newCipherForConfig ( opt * Options ) ( * Cipher , error ) {
2018-05-14 17:06:57 +00:00
mode , err := NewNameEncryptionMode ( opt . FilenameEncryption )
2017-11-06 07:35:53 +00:00
if err != nil {
return nil , err
}
2018-05-14 17:06:57 +00:00
if opt . Password == "" {
2016-07-25 18:18:56 +00:00
return nil , errors . New ( "password not set in config file" )
}
2018-05-14 17:06:57 +00:00
password , err := obscure . Reveal ( opt . Password )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to decrypt password" )
}
2018-05-14 17:06:57 +00:00
var salt string
if opt . Password2 != "" {
salt , err = obscure . Reveal ( opt . Password2 )
2016-08-19 19:02:02 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to decrypt password2" )
}
}
2018-05-14 17:06:57 +00:00
cipher , err := newCipher ( mode , password , salt , opt . DirectoryNameEncryption )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to make cipher" )
}
2018-02-25 11:57:14 +00:00
return cipher , nil
}
2018-05-14 17:06:57 +00:00
// NewCipher constructs a Cipher for the given config
2020-04-09 15:17:17 +00:00
func NewCipher ( m configmap . Mapper ) ( * Cipher , error ) {
2018-05-14 17:06:57 +00:00
// Parse config into Options struct
opt := new ( Options )
err := configstruct . Set ( m , opt )
if err != nil {
return nil , err
}
return newCipherForConfig ( opt )
}
2019-02-07 17:41:17 +00:00
// NewFs constructs an Fs from the path, container:path
2018-05-14 17:06:57 +00:00
func NewFs ( name , rpath string , m configmap . Mapper ) ( fs . Fs , error ) {
// Parse config into Options struct
opt := new ( Options )
err := configstruct . Set ( m , opt )
if err != nil {
return nil , err
}
cipher , err := newCipherForConfig ( opt )
2018-02-25 11:57:14 +00:00
if err != nil {
return nil , err
}
2018-05-14 17:06:57 +00:00
remote := opt . Remote
2016-12-19 14:09:59 +00:00
if strings . HasPrefix ( remote , name + ":" ) {
return nil , errors . New ( "can't point crypt remote at itself - check the value of the remote setting" )
}
2018-09-08 11:02:19 +00:00
wInfo , wName , wPath , wConfig , err := fs . ConfigFs ( remote )
if err != nil {
return nil , errors . Wrapf ( err , "failed to parse remote %q to wrap" , remote )
}
2020-01-21 21:32:34 +00:00
// Make sure to remove trailing . reffering to the current dir
if path . Base ( rpath ) == "." {
rpath = strings . TrimSuffix ( rpath , "." )
}
2016-08-20 17:46:10 +00:00
// Look for a file first
2018-10-09 11:35:27 +00:00
remotePath := fspath . JoinRootPath ( wPath , cipher . EncryptFileName ( rpath ) )
2018-09-08 11:02:19 +00:00
wrappedFs , err := wInfo . NewFs ( wName , remotePath , wConfig )
2016-08-20 17:46:10 +00:00
// if that didn't produce a file, look for a directory
if err != fs . ErrorIsFile {
2018-10-09 11:35:27 +00:00
remotePath = fspath . JoinRootPath ( wPath , cipher . EncryptDirName ( rpath ) )
2018-09-08 11:02:19 +00:00
wrappedFs , err = wInfo . NewFs ( wName , remotePath , wConfig )
2016-08-20 17:46:10 +00:00
}
2016-07-25 18:18:56 +00:00
if err != fs . ErrorIsFile && err != nil {
2018-09-08 11:02:19 +00:00
return nil , errors . Wrapf ( err , "failed to make remote %s:%q to wrap" , wName , remotePath )
2016-07-25 18:18:56 +00:00
}
f := & Fs {
2016-08-20 17:46:10 +00:00
Fs : wrappedFs ,
2016-09-09 07:38:18 +00:00
name : name ,
root : rpath ,
2018-05-14 17:06:57 +00:00
opt : * opt ,
2017-01-13 17:21:47 +00:00
cipher : cipher ,
2016-07-25 18:18:56 +00:00
}
2017-01-13 17:21:47 +00:00
// the features here are ones we could support, and they are
// ANDed with the ones from wrappedFs
f . features = ( & fs . Features {
2018-02-25 11:57:14 +00:00
CaseInsensitive : cipher . NameEncryptionMode ( ) == NameEncryptionOff ,
2017-08-09 14:27:43 +00:00
DuplicateFiles : true ,
ReadMimeType : false , // MimeTypes not supported with crypt
WriteMimeType : false ,
BucketBased : true ,
CanHaveEmptyDirectories : true ,
2019-03-20 12:40:52 +00:00
SetTier : true ,
GetTier : true ,
2020-06-27 10:40:15 +00:00
ServerSideAcrossConfigs : opt . ServerSideAcrossConfigs ,
2017-12-06 15:14:34 +00:00
} ) . Fill ( f ) . Mask ( wrappedFs ) . WrapsFs ( f , wrappedFs )
2017-10-16 19:52:12 +00:00
2016-07-25 18:18:56 +00:00
return f , err
}
2018-05-14 17:06:57 +00:00
// Options defines the configuration for this backend
type Options struct {
Remote string ` config:"remote" `
FilenameEncryption string ` config:"filename_encryption" `
DirectoryNameEncryption bool ` config:"directory_name_encryption" `
Password string ` config:"password" `
Password2 string ` config:"password2" `
2020-06-27 10:40:15 +00:00
ServerSideAcrossConfigs bool ` config:"server_side_across_configs" `
2018-05-14 17:06:57 +00:00
ShowMapping bool ` config:"show_mapping" `
}
2016-07-25 18:18:56 +00:00
// Fs represents a wrapped fs.Fs
type Fs struct {
fs . Fs
2019-03-20 09:57:30 +00:00
wrapper fs . Fs
2017-01-13 17:21:47 +00:00
name string
root string
2018-05-14 17:06:57 +00:00
opt Options
2017-01-13 17:21:47 +00:00
features * fs . Features // optional features
2020-04-09 15:17:17 +00:00
cipher * Cipher
2016-09-09 07:38:18 +00:00
}
// Name of the remote (as passed into NewFs)
func ( f * Fs ) Name ( ) string {
return f . name
}
// Root of the remote (as passed into NewFs)
func ( f * Fs ) Root ( ) string {
return f . root
2016-07-25 18:18:56 +00:00
}
2017-01-13 17:21:47 +00:00
// Features returns the optional features of this Fs
func ( f * Fs ) Features ( ) * fs . Features {
return f . features
}
2016-07-25 18:18:56 +00:00
// String returns a description of the FS
func ( f * Fs ) String ( ) string {
2017-04-10 16:52:31 +00:00
return fmt . Sprintf ( "Encrypted drive '%s:%s'" , f . name , f . root )
2016-07-25 18:18:56 +00:00
}
2017-06-11 21:43:31 +00:00
// Encrypt an object file name to entries.
func ( f * Fs ) add ( entries * fs . DirEntries , obj fs . Object ) {
remote := obj . Remote ( )
decryptedRemote , err := f . cipher . DecryptFileName ( remote )
if err != nil {
fs . Debugf ( remote , "Skipping undecryptable file name: %v" , err )
return
}
2018-05-14 17:06:57 +00:00
if f . opt . ShowMapping {
2017-06-11 21:43:31 +00:00
fs . Logf ( decryptedRemote , "Encrypts to %q" , remote )
}
* entries = append ( * entries , f . newObject ( obj ) )
}
2020-05-20 10:39:20 +00:00
// Encrypt a directory file name to entries.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) addDir ( ctx context . Context , entries * fs . DirEntries , dir fs . Directory ) {
2017-06-30 12:37:29 +00:00
remote := dir . Remote ( )
2017-06-11 21:43:31 +00:00
decryptedRemote , err := f . cipher . DecryptDirName ( remote )
if err != nil {
fs . Debugf ( remote , "Skipping undecryptable dir name: %v" , err )
return
}
2018-05-14 17:06:57 +00:00
if f . opt . ShowMapping {
2017-06-11 21:43:31 +00:00
fs . Logf ( decryptedRemote , "Encrypts to %q" , remote )
}
2019-06-17 08:34:30 +00:00
* entries = append ( * entries , f . newDir ( ctx , dir ) )
2017-06-11 21:43:31 +00:00
}
2017-06-15 15:45:21 +00:00
// Encrypt some directory entries. This alters entries returning it as newEntries.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) encryptEntries ( ctx context . Context , entries fs . DirEntries ) ( newEntries fs . DirEntries , err error ) {
2017-06-15 15:45:21 +00:00
newEntries = entries [ : 0 ] // in place filter
2017-06-11 21:43:31 +00:00
for _ , entry := range entries {
switch x := entry . ( type ) {
case fs . Object :
f . add ( & newEntries , x )
2017-06-30 12:37:29 +00:00
case fs . Directory :
2019-06-17 08:34:30 +00:00
f . addDir ( ctx , & newEntries , x )
2017-06-11 21:43:31 +00:00
default :
return nil , errors . Errorf ( "Unknown object type %T" , entry )
}
}
return newEntries , nil
}
// List the objects and directories in dir into entries. The
// entries can be returned in any order but should be for a
// complete directory.
//
// dir should be "" to list the root, and should not have
// trailing slashes.
//
// This should return ErrDirNotFound if the directory isn't
// found.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) List ( ctx context . Context , dir string ) ( entries fs . DirEntries , err error ) {
entries , err = f . Fs . List ( ctx , f . cipher . EncryptDirName ( dir ) )
2017-06-11 21:43:31 +00:00
if err != nil {
return nil , err
}
2019-06-17 08:34:30 +00:00
return f . encryptEntries ( ctx , entries )
2017-06-11 21:43:31 +00:00
}
// ListR lists the objects and directories of the Fs starting
// from dir recursively into out.
//
// dir should be "" to start from the root, and should not
// have trailing slashes.
//
// This should return ErrDirNotFound if the directory isn't
// found.
//
// It should call callback for each tranche of entries read.
// These need not be returned in any particular order. If
// callback returns an error then the listing will stop
// immediately.
//
// Don't implement this unless you have a more efficient way
// of listing recursively that doing a directory traversal.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) ListR ( ctx context . Context , dir string , callback fs . ListRCallback ) ( err error ) {
return f . Fs . Features ( ) . ListR ( ctx , f . cipher . EncryptDirName ( dir ) , func ( entries fs . DirEntries ) error {
newEntries , err := f . encryptEntries ( ctx , entries )
2017-06-11 21:43:31 +00:00
if err != nil {
return err
}
return callback ( newEntries )
} )
2016-07-25 18:18:56 +00:00
}
// NewObject finds the Object at remote.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) NewObject ( ctx context . Context , remote string ) ( fs . Object , error ) {
o , err := f . Fs . NewObject ( ctx , f . cipher . EncryptFileName ( remote ) )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
return f . newObject ( o ) , nil
}
2019-06-17 08:34:30 +00:00
type putFn func ( ctx context . Context , in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error )
2017-09-27 15:46:28 +00:00
// put implements Put or PutStream
2019-06-17 08:34:30 +00:00
func ( f * Fs ) put ( ctx context . Context , in io . Reader , src fs . ObjectInfo , options [ ] fs . OpenOption , put putFn ) ( fs . Object , error ) {
2018-05-12 11:01:14 +00:00
// Encrypt the data into wrappedIn
2020-04-09 15:43:34 +00:00
wrappedIn , encrypter , err := f . cipher . encryptData ( in )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
2018-05-12 11:01:14 +00:00
// Find a hash the destination supports to compute a hash of
// the encrypted data
ht := f . Fs . Hashes ( ) . GetOne ( )
var hasher * hash . MultiHasher
if ht != hash . None {
hasher , err = hash . NewMultiHasherTypes ( hash . NewHashSet ( ht ) )
if err != nil {
return nil , err
}
2018-08-20 14:29:35 +00:00
// unwrap the accounting
var wrap accounting . WrapFn
wrappedIn , wrap = accounting . UnWrap ( wrappedIn )
// add the hasher
2018-05-12 11:01:14 +00:00
wrappedIn = io . TeeReader ( wrappedIn , hasher )
2018-08-20 14:29:35 +00:00
// wrap the accounting back on
wrappedIn = wrap ( wrappedIn )
2018-05-12 11:01:14 +00:00
}
// Transfer the data
2020-04-09 15:43:34 +00:00
o , err := put ( ctx , wrappedIn , f . newObjectInfo ( src , encrypter . nonce ) , options ... )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
2018-05-12 11:01:14 +00:00
// Check the hashes of the encrypted data if we were comparing them
if ht != hash . None && hasher != nil {
srcHash := hasher . Sums ( ) [ ht ]
var dstHash string
2019-06-17 08:34:30 +00:00
dstHash , err = o . Hash ( ctx , ht )
2018-05-12 11:01:14 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to read destination hash" )
}
if srcHash != "" && dstHash != "" && srcHash != dstHash {
// remove object
2019-06-17 08:34:30 +00:00
err = o . Remove ( ctx )
2018-05-12 11:01:14 +00:00
if err != nil {
fs . Errorf ( o , "Failed to remove corrupted object: %v" , err )
}
return nil , errors . Errorf ( "corrupted on transfer: %v crypted hash differ %q vs %q" , ht , srcHash , dstHash )
}
}
2016-07-25 18:18:56 +00:00
return f . newObject ( o ) , nil
}
2017-09-27 15:46:28 +00:00
// Put in to the remote path with the modTime given of the given size
//
// May create the object even if it returns an error - if so
// will return the object and the error, otherwise will return
// nil and the error
2019-06-17 08:34:30 +00:00
func ( f * Fs ) Put ( ctx context . Context , in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
return f . put ( ctx , in , src , options , f . Fs . Put )
2017-09-27 15:46:28 +00:00
}
2017-08-03 19:42:35 +00:00
// PutStream uploads to the remote path with the modTime given of indeterminate size
2019-06-17 08:34:30 +00:00
func ( f * Fs ) PutStream ( ctx context . Context , in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
return f . put ( ctx , in , src , options , f . Fs . Features ( ) . PutStream )
2017-08-03 19:42:35 +00:00
}
2016-07-25 18:18:56 +00:00
// Hashes returns the supported hash sets.
2018-01-12 16:30:54 +00:00
func ( f * Fs ) Hashes ( ) hash . Set {
2018-01-18 20:27:52 +00:00
return hash . Set ( hash . None )
2016-07-25 18:18:56 +00:00
}
2016-12-06 15:13:29 +00:00
// Mkdir makes the directory (container, bucket)
//
// Shouldn't return an error if it already exists
2019-06-17 08:34:30 +00:00
func ( f * Fs ) Mkdir ( ctx context . Context , dir string ) error {
return f . Fs . Mkdir ( ctx , f . cipher . EncryptDirName ( dir ) )
2016-12-06 15:13:29 +00:00
}
// Rmdir removes the directory (container, bucket) if empty
//
// Return an error if it doesn't exist or isn't empty
2019-06-17 08:34:30 +00:00
func ( f * Fs ) Rmdir ( ctx context . Context , dir string ) error {
return f . Fs . Rmdir ( ctx , f . cipher . EncryptDirName ( dir ) )
2016-12-06 15:13:29 +00:00
}
2020-06-04 21:25:14 +00:00
// Purge all files in the directory specified
2016-07-25 18:18:56 +00:00
//
// Implement this if you have a way of deleting all the files
// quicker than just running Remove() on the result of List()
//
// Return an error if it doesn't exist
2020-06-04 21:25:14 +00:00
func ( f * Fs ) Purge ( ctx context . Context , dir string ) error {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . Purge
if do == nil {
2016-07-25 18:18:56 +00:00
return fs . ErrorCantPurge
}
2020-06-04 21:25:14 +00:00
return do ( ctx , dir )
2016-07-25 18:18:56 +00:00
}
// Copy src to this remote using server side copy operations.
//
// This is stored with the remote path given
//
// It returns the destination Object and a possible error
//
// Will only be called if src.Fs().Name() == f.Name()
//
// If it isn't possible then return fs.ErrorCantCopy
2019-06-17 08:34:30 +00:00
func ( f * Fs ) Copy ( ctx context . Context , src fs . Object , remote string ) ( fs . Object , error ) {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . Copy
if do == nil {
2016-07-25 18:18:56 +00:00
return nil , fs . ErrorCantCopy
}
o , ok := src . ( * Object )
if ! ok {
return nil , fs . ErrorCantCopy
}
2019-06-17 08:34:30 +00:00
oResult , err := do ( ctx , o . Object , f . cipher . EncryptFileName ( remote ) )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
return f . newObject ( oResult ) , nil
}
// Move src to this remote using server side move operations.
//
// This is stored with the remote path given
//
// It returns the destination Object and a possible error
//
// Will only be called if src.Fs().Name() == f.Name()
//
// If it isn't possible then return fs.ErrorCantMove
2019-06-17 08:34:30 +00:00
func ( f * Fs ) Move ( ctx context . Context , src fs . Object , remote string ) ( fs . Object , error ) {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . Move
if do == nil {
2016-07-25 18:18:56 +00:00
return nil , fs . ErrorCantMove
}
o , ok := src . ( * Object )
if ! ok {
2016-08-23 16:43:43 +00:00
return nil , fs . ErrorCantMove
2016-07-25 18:18:56 +00:00
}
2019-06-17 08:34:30 +00:00
oResult , err := do ( ctx , o . Object , f . cipher . EncryptFileName ( remote ) )
2016-07-25 18:18:56 +00:00
if err != nil {
return nil , err
}
return f . newObject ( oResult ) , nil
}
2017-02-05 21:20:56 +00:00
// DirMove moves src, srcRemote to this remote at dstRemote
// using server side move operations.
2016-08-23 16:43:43 +00:00
//
// Will only be called if src.Fs().Name() == f.Name()
//
// If it isn't possible then return fs.ErrorCantDirMove
//
// If destination exists then return fs.ErrorDirExists
2019-06-17 08:34:30 +00:00
func ( f * Fs ) DirMove ( ctx context . Context , src fs . Fs , srcRemote , dstRemote string ) error {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . DirMove
if do == nil {
2016-08-23 16:43:43 +00:00
return fs . ErrorCantDirMove
}
srcFs , ok := src . ( * Fs )
if ! ok {
2017-02-09 11:01:20 +00:00
fs . Debugf ( srcFs , "Can't move directory - not same remote type" )
2016-08-23 16:43:43 +00:00
return fs . ErrorCantDirMove
}
2019-06-17 08:34:30 +00:00
return do ( ctx , srcFs . Fs , f . cipher . EncryptDirName ( srcRemote ) , f . cipher . EncryptDirName ( dstRemote ) )
2017-01-13 17:21:47 +00:00
}
// PutUnchecked uploads the object
//
// This will create a duplicate if we upload a new file without
// checking to see if there is one already - use Put() for that.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) PutUnchecked ( ctx context . Context , in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . PutUnchecked
if do == nil {
return nil , errors . New ( "can't PutUnchecked" )
}
2020-04-09 15:43:34 +00:00
wrappedIn , encrypter , err := f . cipher . encryptData ( in )
2017-01-13 17:21:47 +00:00
if err != nil {
return nil , err
}
2020-04-09 15:43:34 +00:00
o , err := do ( ctx , wrappedIn , f . newObjectInfo ( src , encrypter . nonce ) )
2017-01-13 17:21:47 +00:00
if err != nil {
return nil , err
}
return f . newObject ( o ) , nil
}
// CleanUp the trash in the Fs
//
// Implement this if you have a way of emptying the trash or
// otherwise cleaning up old versions of files.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) CleanUp ( ctx context . Context ) error {
2017-01-13 17:21:47 +00:00
do := f . Fs . Features ( ) . CleanUp
if do == nil {
return errors . New ( "can't CleanUp" )
}
2019-06-17 08:34:30 +00:00
return do ( ctx )
2016-08-23 16:43:43 +00:00
}
2018-04-16 21:19:25 +00:00
// About gets quota information from the Fs
2019-06-17 08:34:30 +00:00
func ( f * Fs ) About ( ctx context . Context ) ( * fs . Usage , error ) {
2018-04-16 21:19:25 +00:00
do := f . Fs . Features ( ) . About
if do == nil {
return nil , errors . New ( "About not supported" )
}
2019-06-17 08:34:30 +00:00
return do ( ctx )
2018-04-16 21:19:25 +00:00
}
2016-07-25 18:18:56 +00:00
// UnWrap returns the Fs that this Fs is wrapping
func ( f * Fs ) UnWrap ( ) fs . Fs {
return f . Fs
}
2019-03-20 09:57:30 +00:00
// WrapFs returns the Fs that is wrapping this Fs
func ( f * Fs ) WrapFs ( ) fs . Fs {
return f . wrapper
}
// SetWrapper sets the Fs that is wrapping this Fs
func ( f * Fs ) SetWrapper ( wrapper fs . Fs ) {
f . wrapper = wrapper
}
2018-01-08 08:20:01 +00:00
// EncryptFileName returns an encrypted file name
func ( f * Fs ) EncryptFileName ( fileName string ) string {
return f . cipher . EncryptFileName ( fileName )
}
2017-09-20 11:40:07 +00:00
// DecryptFileName returns a decrypted file name
func ( f * Fs ) DecryptFileName ( encryptedFileName string ) ( string , error ) {
return f . cipher . DecryptFileName ( encryptedFileName )
}
2020-04-09 15:43:34 +00:00
// computeHashWithNonce takes the nonce and encrypts the contents of
// src with it, and calculates the hash given by HashType on the fly
//
// Note that we break lots of encapsulation in this function.
func ( f * Fs ) computeHashWithNonce ( ctx context . Context , nonce nonce , src fs . Object , hashType hash . Type ) ( hashStr string , err error ) {
// Open the src for input
in , err := src . Open ( ctx )
if err != nil {
return "" , errors . Wrap ( err , "failed to open src" )
}
defer fs . CheckClose ( in , & err )
// Now encrypt the src with the nonce
out , err := f . cipher . newEncrypter ( in , & nonce )
if err != nil {
return "" , errors . Wrap ( err , "failed to make encrypter" )
}
// pipe into hash
m , err := hash . NewMultiHasherTypes ( hash . NewHashSet ( hashType ) )
if err != nil {
return "" , errors . Wrap ( err , "failed to make hasher" )
}
_ , err = io . Copy ( m , out )
if err != nil {
return "" , errors . Wrap ( err , "failed to hash data" )
}
return m . Sums ( ) [ hashType ] , nil
}
2017-02-12 16:30:18 +00:00
// ComputeHash takes the nonce from o, and encrypts the contents of
2019-02-07 17:41:17 +00:00
// src with it, and calculates the hash given by HashType on the fly
2017-02-12 16:30:18 +00:00
//
// Note that we break lots of encapsulation in this function.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) ComputeHash ( ctx context . Context , o * Object , src fs . Object , hashType hash . Type ) ( hashStr string , err error ) {
2017-02-12 16:30:18 +00:00
// Read the nonce - opening the file is sufficient to read the nonce in
2018-02-19 17:37:07 +00:00
// use a limited read so we only read the header
2019-06-17 08:34:30 +00:00
in , err := o . Object . Open ( ctx , & fs . RangeOption { Start : 0 , End : int64 ( fileHeaderSize ) - 1 } )
2017-02-12 16:30:18 +00:00
if err != nil {
2018-02-19 17:37:07 +00:00
return "" , errors . Wrap ( err , "failed to open object to read nonce" )
2017-02-12 16:30:18 +00:00
}
2020-04-09 15:17:17 +00:00
d , err := f . cipher . newDecrypter ( in )
2018-02-19 17:37:07 +00:00
if err != nil {
_ = in . Close ( )
return "" , errors . Wrap ( err , "failed to open object to read nonce" )
}
nonce := d . nonce
2017-02-12 16:30:18 +00:00
// fs.Debugf(o, "Read nonce % 2x", nonce)
// Check nonce isn't all zeros
isZero := true
for i := range nonce {
if nonce [ i ] != 0 {
isZero = false
}
}
if isZero {
fs . Errorf ( o , "empty nonce read" )
}
2018-02-19 17:37:07 +00:00
// Close d (and hence in) once we have read the nonce
err = d . Close ( )
2017-02-12 16:30:18 +00:00
if err != nil {
return "" , errors . Wrap ( err , "failed to close nonce read" )
}
2020-04-09 15:43:34 +00:00
return f . computeHashWithNonce ( ctx , nonce , src , hashType )
2017-02-12 16:30:18 +00:00
}
2019-03-20 09:57:30 +00:00
// MergeDirs merges the contents of all the directories passed
// in into the first one and rmdirs the other directories.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) MergeDirs ( ctx context . Context , dirs [ ] fs . Directory ) error {
2019-03-20 09:57:30 +00:00
do := f . Fs . Features ( ) . MergeDirs
if do == nil {
return errors . New ( "MergeDirs not supported" )
}
out := make ( [ ] fs . Directory , len ( dirs ) )
for i , dir := range dirs {
2019-06-17 08:34:30 +00:00
out [ i ] = fs . NewDirCopy ( ctx , dir ) . SetRemote ( f . cipher . EncryptDirName ( dir . Remote ( ) ) )
2019-03-20 09:57:30 +00:00
}
2019-06-17 08:34:30 +00:00
return do ( ctx , out )
2019-03-20 09:57:30 +00:00
}
// DirCacheFlush resets the directory cache - used in testing
// as an optional interface
func ( f * Fs ) DirCacheFlush ( ) {
do := f . Fs . Features ( ) . DirCacheFlush
if do != nil {
do ( )
}
}
2019-03-20 11:59:26 +00:00
// PublicLink generates a public link to the remote path (usually readable by anyone)
2020-05-31 21:18:01 +00:00
func ( f * Fs ) PublicLink ( ctx context . Context , remote string , expire fs . Duration , unlink bool ) ( string , error ) {
2019-03-20 11:59:26 +00:00
do := f . Fs . Features ( ) . PublicLink
if do == nil {
return "" , errors . New ( "PublicLink not supported" )
}
2019-06-17 08:34:30 +00:00
o , err := f . NewObject ( ctx , remote )
2019-03-20 11:59:26 +00:00
if err != nil {
// assume it is a directory
2020-05-31 21:18:01 +00:00
return do ( ctx , f . cipher . EncryptDirName ( remote ) , expire , unlink )
2019-03-20 11:59:26 +00:00
}
2020-05-31 21:18:01 +00:00
return do ( ctx , o . ( * Object ) . Object . Remote ( ) , expire , unlink )
2019-03-20 11:59:26 +00:00
}
2019-03-20 11:33:25 +00:00
// ChangeNotify calls the passed function with a path
// that has had changes. If the implementation
// uses polling, it should adhere to the given interval.
2019-06-17 08:34:30 +00:00
func ( f * Fs ) ChangeNotify ( ctx context . Context , notifyFunc func ( string , fs . EntryType ) , pollIntervalChan <- chan time . Duration ) {
2019-03-20 11:33:25 +00:00
do := f . Fs . Features ( ) . ChangeNotify
if do == nil {
return
}
wrappedNotifyFunc := func ( path string , entryType fs . EntryType ) {
2019-05-12 15:50:03 +00:00
// fs.Debugf(f, "ChangeNotify: path %q entryType %d", path, entryType)
2019-03-20 11:33:25 +00:00
var (
err error
decrypted string
)
switch entryType {
case fs . EntryDirectory :
decrypted , err = f . cipher . DecryptDirName ( path )
case fs . EntryObject :
decrypted , err = f . cipher . DecryptFileName ( path )
default :
fs . Errorf ( path , "crypt ChangeNotify: ignoring unknown EntryType %d" , entryType )
return
}
if err != nil {
fs . Logf ( f , "ChangeNotify was unable to decrypt %q: %s" , path , err )
return
}
notifyFunc ( decrypted , entryType )
}
2019-06-17 08:34:30 +00:00
do ( ctx , wrappedNotifyFunc , pollIntervalChan )
2019-03-20 11:33:25 +00:00
}
2020-04-29 17:54:16 +00:00
var commandHelp = [ ] fs . CommandHelp {
{
Name : "encode" ,
Short : "Encode the given filename(s)" ,
2020-05-13 17:11:45 +00:00
Long : ` This encodes the filenames given as arguments returning a list of
strings of the encoded results .
2020-04-29 17:54:16 +00:00
2020-05-13 17:11:45 +00:00
Usage Example :
2020-04-29 17:54:16 +00:00
2020-05-13 17:11:45 +00:00
rclone backend encode crypt : file1 [ file2 ... ]
rclone rc backend / command command = encode fs = crypt : file1 [ file2 ... ]
` ,
2020-04-29 17:54:16 +00:00
} ,
{
Name : "decode" ,
Short : "Decode the given filename(s)" ,
2020-05-13 17:11:45 +00:00
Long : ` This decodes the filenames given as arguments returning a list of
strings of the decoded results . It will return an error if any of the
inputs are invalid .
2020-04-29 17:54:16 +00:00
2020-05-13 17:11:45 +00:00
Usage Example :
2020-04-29 17:54:16 +00:00
2020-05-13 17:11:45 +00:00
rclone backend decode crypt : encryptedfile1 [ encryptedfile2 ... ]
rclone rc backend / command command = decode fs = crypt : encryptedfile1 [ encryptedfile2 ... ]
` ,
2020-04-29 17:54:16 +00:00
} ,
}
// Command the backend to run a named command
//
// The command run is name
// args may be used to read arguments from
// opts may be used to read optional arguments from
//
// The result should be capable of being JSON encoded
// If it is a string or a []string it will be shown to the user
// otherwise it will be JSON encoded and shown to the user like that
func ( f * Fs ) Command ( ctx context . Context , name string , arg [ ] string , opt map [ string ] string ) ( out interface { } , err error ) {
switch name {
case "decode" :
2020-05-13 17:11:45 +00:00
out := make ( [ ] string , 0 , len ( arg ) )
2020-04-29 17:54:16 +00:00
for _ , encryptedFileName := range arg {
fileName , err := f . DecryptFileName ( encryptedFileName )
if err != nil {
return out , errors . Wrap ( err , fmt . Sprintf ( "Failed to decrypt : %s" , encryptedFileName ) )
}
2020-05-13 17:11:45 +00:00
out = append ( out , fileName )
2020-04-29 17:54:16 +00:00
}
return out , nil
case "encode" :
2020-05-13 17:11:45 +00:00
out := make ( [ ] string , 0 , len ( arg ) )
2020-04-29 17:54:16 +00:00
for _ , fileName := range arg {
encryptedFileName := f . EncryptFileName ( fileName )
2020-05-13 17:11:45 +00:00
out = append ( out , encryptedFileName )
2020-04-29 17:54:16 +00:00
}
return out , nil
default :
return nil , fs . ErrorCommandNotFound
}
}
2016-07-25 18:18:56 +00:00
// Object describes a wrapped for being read from the Fs
//
// This decrypts the remote name and decrypts the data
type Object struct {
fs . Object
f * Fs
}
func ( f * Fs ) newObject ( o fs . Object ) * Object {
return & Object {
Object : o ,
f : f ,
}
}
// Fs returns read only access to the Fs that this object is part of
func ( o * Object ) Fs ( ) fs . Info {
return o . f
}
// Return a string version
func ( o * Object ) String ( ) string {
if o == nil {
return "<nil>"
}
return o . Remote ( )
}
// Remote returns the remote path
func ( o * Object ) Remote ( ) string {
remote := o . Object . Remote ( )
2016-08-20 17:46:10 +00:00
decryptedName , err := o . f . cipher . DecryptFileName ( remote )
2016-07-25 18:18:56 +00:00
if err != nil {
2017-02-09 11:01:20 +00:00
fs . Debugf ( remote , "Undecryptable file name: %v" , err )
2016-07-25 18:18:56 +00:00
return remote
}
return decryptedName
}
// Size returns the size of the file
func ( o * Object ) Size ( ) int64 {
size , err := o . f . cipher . DecryptedSize ( o . Object . Size ( ) )
if err != nil {
2017-02-09 11:01:20 +00:00
fs . Debugf ( o , "Bad size for decrypt: %v" , err )
2016-07-25 18:18:56 +00:00
}
return size
}
// Hash returns the selected checksum of the file
// If no checksum is available it returns ""
2019-06-17 08:34:30 +00:00
func ( o * Object ) Hash ( ctx context . Context , ht hash . Type ) ( string , error ) {
2018-01-18 20:27:52 +00:00
return "" , hash . ErrUnsupported
2016-07-25 18:18:56 +00:00
}
2017-02-12 16:30:18 +00:00
// UnWrap returns the wrapped Object
func ( o * Object ) UnWrap ( ) fs . Object {
return o . Object
}
2016-07-25 18:18:56 +00:00
// Open opens the file for read. Call Close() on the returned io.ReadCloser
2019-06-17 08:34:30 +00:00
func ( o * Object ) Open ( ctx context . Context , options ... fs . OpenOption ) ( rc io . ReadCloser , err error ) {
2018-01-22 18:31:59 +00:00
var openOptions [ ] fs . OpenOption
var offset , limit int64 = 0 , - 1
2016-09-10 10:29:57 +00:00
for _ , option := range options {
switch x := option . ( type ) {
case * fs . SeekOption :
offset = x . Offset
2018-01-22 18:31:59 +00:00
case * fs . RangeOption :
offset , limit = x . Decode ( o . Size ( ) )
2016-09-10 10:29:57 +00:00
default :
2018-01-22 18:31:59 +00:00
// pass on Options to underlying open if appropriate
openOptions = append ( openOptions , option )
2016-09-10 10:29:57 +00:00
}
}
2019-06-17 08:34:30 +00:00
rc , err = o . f . cipher . DecryptDataSeek ( ctx , func ( ctx context . Context , underlyingOffset , underlyingLimit int64 ) ( io . ReadCloser , error ) {
2018-01-22 18:31:59 +00:00
if underlyingOffset == 0 && underlyingLimit < 0 {
2016-10-20 16:47:33 +00:00
// Open with no seek
2019-06-17 08:34:30 +00:00
return o . Object . Open ( ctx , openOptions ... )
2018-01-22 18:31:59 +00:00
}
// Open stream with a range of underlyingOffset, underlyingLimit
end := int64 ( - 1 )
if underlyingLimit >= 0 {
end = underlyingOffset + underlyingLimit - 1
if end >= o . Object . Size ( ) {
end = - 1
}
2016-10-20 16:47:33 +00:00
}
2018-01-22 18:31:59 +00:00
newOpenOptions := append ( openOptions , & fs . RangeOption { Start : underlyingOffset , End : end } )
2019-06-17 08:34:30 +00:00
return o . Object . Open ( ctx , newOpenOptions ... )
2018-01-22 18:31:59 +00:00
} , offset , limit )
2016-09-10 10:29:57 +00:00
if err != nil {
return nil , err
}
2018-01-22 18:31:59 +00:00
return rc , nil
2016-07-25 18:18:56 +00:00
}
// Update in to the object with the modTime given of the given size
2019-06-17 08:34:30 +00:00
func ( o * Object ) Update ( ctx context . Context , in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) error {
update := func ( ctx context . Context , in io . Reader , src fs . ObjectInfo , options ... fs . OpenOption ) ( fs . Object , error ) {
return o . Object , o . Object . Update ( ctx , in , src , options ... )
2016-07-25 18:18:56 +00:00
}
2019-06-17 08:34:30 +00:00
_ , err := o . f . put ( ctx , in , src , options , update )
2018-05-12 11:01:14 +00:00
return err
2016-07-25 18:18:56 +00:00
}
// newDir returns a dir with the Name decrypted
2019-06-17 08:34:30 +00:00
func ( f * Fs ) newDir ( ctx context . Context , dir fs . Directory ) fs . Directory {
newDir := fs . NewDirCopy ( ctx , dir )
2017-06-30 12:37:29 +00:00
remote := dir . Remote ( )
2016-08-20 17:46:10 +00:00
decryptedRemote , err := f . cipher . DecryptDirName ( remote )
2016-07-25 18:18:56 +00:00
if err != nil {
2017-02-09 11:01:20 +00:00
fs . Debugf ( remote , "Undecryptable dir name: %v" , err )
2016-07-25 18:18:56 +00:00
} else {
2018-08-04 10:16:43 +00:00
newDir . SetRemote ( decryptedRemote )
2016-07-25 18:18:56 +00:00
}
2018-08-04 10:16:43 +00:00
return newDir
2016-07-25 18:18:56 +00:00
}
2019-08-12 10:07:31 +00:00
// UserInfo returns info about the connected user
func ( f * Fs ) UserInfo ( ctx context . Context ) ( map [ string ] string , error ) {
do := f . Fs . Features ( ) . UserInfo
if do == nil {
return nil , fs . ErrorNotImplemented
}
return do ( ctx )
}
// Disconnect the current user
func ( f * Fs ) Disconnect ( ctx context . Context ) error {
do := f . Fs . Features ( ) . Disconnect
if do == nil {
return fs . ErrorNotImplemented
}
return do ( ctx )
}
2016-07-25 18:18:56 +00:00
// ObjectInfo describes a wrapped fs.ObjectInfo for being the source
//
// This encrypts the remote name and adjusts the size
type ObjectInfo struct {
fs . ObjectInfo
2020-04-09 15:43:34 +00:00
f * Fs
nonce nonce
2016-07-25 18:18:56 +00:00
}
2020-04-09 15:43:34 +00:00
func ( f * Fs ) newObjectInfo ( src fs . ObjectInfo , nonce nonce ) * ObjectInfo {
2016-07-25 18:18:56 +00:00
return & ObjectInfo {
ObjectInfo : src ,
f : f ,
2020-04-09 15:43:34 +00:00
nonce : nonce ,
2016-07-25 18:18:56 +00:00
}
}
// Fs returns read only access to the Fs that this object is part of
func ( o * ObjectInfo ) Fs ( ) fs . Info {
return o . f
}
// Remote returns the remote path
func ( o * ObjectInfo ) Remote ( ) string {
2016-08-20 17:46:10 +00:00
return o . f . cipher . EncryptFileName ( o . ObjectInfo . Remote ( ) )
2016-07-25 18:18:56 +00:00
}
// Size returns the size of the file
func ( o * ObjectInfo ) Size ( ) int64 {
2017-09-27 15:46:28 +00:00
size := o . ObjectInfo . Size ( )
if size < 0 {
return size
}
return o . f . cipher . EncryptedSize ( size )
2016-07-25 18:18:56 +00:00
}
2016-08-25 20:26:55 +00:00
// Hash returns the selected checksum of the file
// If no checksum is available it returns ""
2019-06-17 08:34:30 +00:00
func ( o * ObjectInfo ) Hash ( ctx context . Context , hash hash . Type ) ( string , error ) {
2020-04-09 15:43:34 +00:00
var srcObj fs . Object
var ok bool
// Get the underlying object if there is one
if srcObj , ok = o . ObjectInfo . ( fs . Object ) ; ok {
// Prefer direct interface assertion
} else if do , ok := o . ObjectInfo . ( fs . ObjectUnWrapper ) ; ok {
2020-05-20 10:39:20 +00:00
// Otherwise likely is an operations.OverrideRemote
2020-04-09 15:43:34 +00:00
srcObj = do . UnWrap ( )
} else {
return "" , nil
}
// if this is wrapping a local object then we work out the hash
if srcObj . Fs ( ) . Features ( ) . IsLocal {
// Read the data and encrypt it to calculate the hash
fs . Debugf ( o , "Computing %v hash of encrypted source" , hash )
return o . f . computeHashWithNonce ( ctx , o . nonce , srcObj , hash )
}
2016-08-25 20:26:55 +00:00
return "" , nil
}
2019-03-20 12:40:52 +00:00
// ID returns the ID of the Object if known, or "" if not
func ( o * Object ) ID ( ) string {
do , ok := o . Object . ( fs . IDer )
if ! ok {
return ""
}
return do . ID ( )
}
// SetTier performs changing storage tier of the Object if
// multiple storage classes supported
func ( o * Object ) SetTier ( tier string ) error {
do , ok := o . Object . ( fs . SetTierer )
if ! ok {
return errors . New ( "crypt: underlying remote does not support SetTier" )
}
return do . SetTier ( tier )
}
// GetTier returns storage tier or class of the Object
func ( o * Object ) GetTier ( ) string {
do , ok := o . Object . ( fs . GetTierer )
if ! ok {
return ""
}
return do . GetTier ( )
}
2016-07-25 18:18:56 +00:00
// Check the interfaces are satisfied
var (
2018-01-31 14:22:49 +00:00
_ fs . Fs = ( * Fs ) ( nil )
_ fs . Purger = ( * Fs ) ( nil )
_ fs . Copier = ( * Fs ) ( nil )
_ fs . Mover = ( * Fs ) ( nil )
_ fs . DirMover = ( * Fs ) ( nil )
2020-04-29 17:54:16 +00:00
_ fs . Commander = ( * Fs ) ( nil )
2018-01-31 14:22:49 +00:00
_ fs . PutUncheckeder = ( * Fs ) ( nil )
_ fs . PutStreamer = ( * Fs ) ( nil )
_ fs . CleanUpper = ( * Fs ) ( nil )
_ fs . UnWrapper = ( * Fs ) ( nil )
_ fs . ListRer = ( * Fs ) ( nil )
2018-04-16 21:19:25 +00:00
_ fs . Abouter = ( * Fs ) ( nil )
2019-03-20 11:59:26 +00:00
_ fs . Wrapper = ( * Fs ) ( nil )
_ fs . MergeDirser = ( * Fs ) ( nil )
_ fs . DirCacheFlusher = ( * Fs ) ( nil )
_ fs . ChangeNotifier = ( * Fs ) ( nil )
_ fs . PublicLinker = ( * Fs ) ( nil )
2019-08-12 10:07:31 +00:00
_ fs . UserInfoer = ( * Fs ) ( nil )
_ fs . Disconnecter = ( * Fs ) ( nil )
2018-01-31 14:22:49 +00:00
_ fs . ObjectInfo = ( * ObjectInfo ) ( nil )
_ fs . Object = ( * Object ) ( nil )
_ fs . ObjectUnWrapper = ( * Object ) ( nil )
2019-03-20 12:40:52 +00:00
_ fs . IDer = ( * Object ) ( nil )
_ fs . SetTierer = ( * Object ) ( nil )
_ fs . GetTierer = ( * Object ) ( nil )
2016-07-25 18:18:56 +00:00
)