github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/config/obscure/obscure.go (about)

     1  // Package obscure contains the Obscure and Reveal commands
     2  package obscure
     3  
     4  import (
     5  	"crypto/aes"
     6  	"crypto/cipher"
     7  	"crypto/rand"
     8  	"encoding/base64"
     9  	"io"
    10  	"log"
    11  
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // crypt internals
    16  var (
    17  	cryptKey = []byte{
    18  		0x9c, 0x93, 0x5b, 0x48, 0x73, 0x0a, 0x55, 0x4d,
    19  		0x6b, 0xfd, 0x7c, 0x63, 0xc8, 0x86, 0xa9, 0x2b,
    20  		0xd3, 0x90, 0x19, 0x8e, 0xb8, 0x12, 0x8a, 0xfb,
    21  		0xf4, 0xde, 0x16, 0x2b, 0x8b, 0x95, 0xf6, 0x38,
    22  	}
    23  	cryptBlock cipher.Block
    24  	cryptRand  = rand.Reader
    25  )
    26  
    27  // crypt transforms in to out using iv under AES-CTR.
    28  //
    29  // in and out may be the same buffer.
    30  //
    31  // Note encryption and decryption are the same operation
    32  func crypt(out, in, iv []byte) error {
    33  	if cryptBlock == nil {
    34  		var err error
    35  		cryptBlock, err = aes.NewCipher(cryptKey)
    36  		if err != nil {
    37  			return err
    38  		}
    39  	}
    40  	stream := cipher.NewCTR(cryptBlock, iv)
    41  	stream.XORKeyStream(out, in)
    42  	return nil
    43  }
    44  
    45  // Obscure a value
    46  //
    47  // This is done by encrypting with AES-CTR
    48  func Obscure(x string) (string, error) {
    49  	plaintext := []byte(x)
    50  	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    51  	iv := ciphertext[:aes.BlockSize]
    52  	if _, err := io.ReadFull(cryptRand, iv); err != nil {
    53  		return "", errors.Wrap(err, "failed to read iv")
    54  	}
    55  	if err := crypt(ciphertext[aes.BlockSize:], plaintext, iv); err != nil {
    56  		return "", errors.Wrap(err, "encrypt failed")
    57  	}
    58  	return base64.RawURLEncoding.EncodeToString(ciphertext), nil
    59  }
    60  
    61  // MustObscure obscures a value, exiting with a fatal error if it failed
    62  func MustObscure(x string) string {
    63  	out, err := Obscure(x)
    64  	if err != nil {
    65  		log.Fatalf("Obscure failed: %v", err)
    66  	}
    67  	return out
    68  }
    69  
    70  // Reveal an obscured value
    71  func Reveal(x string) (string, error) {
    72  	ciphertext, err := base64.RawURLEncoding.DecodeString(x)
    73  	if err != nil {
    74  		return "", errors.Wrap(err, "base64 decode failed when revealing password - is it obscured?")
    75  	}
    76  	if len(ciphertext) < aes.BlockSize {
    77  		return "", errors.New("input too short when revealing password - is it obscured?")
    78  	}
    79  	buf := ciphertext[aes.BlockSize:]
    80  	iv := ciphertext[:aes.BlockSize]
    81  	if err := crypt(buf, buf, iv); err != nil {
    82  		return "", errors.Wrap(err, "decrypt failed when revealing password - is it obscured?")
    83  	}
    84  	return string(buf), nil
    85  }
    86  
    87  // MustReveal reveals an obscured value, exiting with a fatal error if it failed
    88  func MustReveal(x string) string {
    89  	out, err := Reveal(x)
    90  	if err != nil {
    91  		log.Fatalf("Reveal failed: %v", err)
    92  	}
    93  	return out
    94  }