diff --git a/cmd/format.go b/cmd/format.go index 843bb3c2be3f..2e6024171f16 100644 --- a/cmd/format.go +++ b/cmd/format.go @@ -147,6 +147,10 @@ Details: https://juicefs.com/docs/community/quick_start_guide`, Name: "encrypt-rsa-key", Usage: "a path to RSA private key (PEM)", }, + &cli.BoolFlag{ + Name: "chacha20", + Usage: "encrypt use chacha20 instead of aes", + }, &cli.IntFlag{ Name: "trash-days", Value: 1, @@ -237,7 +241,7 @@ func createStorage(format meta.Format) (object.ObjectStorage, error) { if err != nil { return nil, fmt.Errorf("incorrect passphrase: %s", err) } - encryptor := object.NewAESEncryptor(object.NewRSAEncryptor(privKey)) + encryptor := object.NewDataEncryptor(object.NewRSAEncryptor(privKey), format.ChaCha20) blob = object.NewEncrypted(blob, encryptor) } return blob, nil @@ -392,6 +396,7 @@ func format(c *cli.Context) error { SecretKey: c.String("secret-key"), SessionToken: c.String("session-token"), EncryptKey: loadEncrypt(c.String("encrypt-rsa-key")), + ChaCha20: c.Bool("chacha20"), Shards: c.Int("shards"), HashPrefix: c.Bool("hash-prefix"), Capacity: c.Uint64("capacity") << 30, diff --git a/pkg/meta/config.go b/pkg/meta/config.go index aae04d7663d4..1e6907faacc9 100644 --- a/pkg/meta/config.go +++ b/pkg/meta/config.go @@ -59,6 +59,7 @@ type Format struct { Inodes uint64 `json:",omitempty"` EncryptKey string `json:",omitempty"` KeyEncrypted bool `json:",omitempty"` + ChaCha20 bool `json:",omitempty"` TrashDays int `json:",omitempty"` MetaVersion int `json:",omitempty"` MinClientVersion string `json:",omitempty"` diff --git a/pkg/object/encrypt.go b/pkg/object/encrypt.go index a8d4a84dc9c8..5199fc034e11 100644 --- a/pkg/object/encrypt.go +++ b/pkg/object/encrypt.go @@ -30,6 +30,8 @@ import ( "io" "io/ioutil" "strings" + + "golang.org/x/crypto/chacha20poly1305" ) type Encryptor interface { @@ -114,16 +116,27 @@ func (e *rsaEncryptor) Decrypt(ciphertext []byte) ([]byte, error) { return rsa.DecryptOAEP(sha256.New(), rand.Reader, e.privKey, ciphertext, e.label) } -type aesEncryptor struct { +type dataEncryptor struct { keyEncryptor Encryptor keyLen int + aead func(key []byte) (cipher.AEAD, error) } -func NewAESEncryptor(keyEncryptor Encryptor) Encryptor { - return &aesEncryptor{keyEncryptor, 32} // AES-256-GCM +func NewDataEncryptor(keyEncryptor Encryptor, chacha20 bool) Encryptor { + if chacha20 { + return &dataEncryptor{keyEncryptor, chacha20poly1305.KeySize, chacha20poly1305.New} + } + aead := func(key []byte) (cipher.AEAD, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewGCM(block) + } + return &dataEncryptor{keyEncryptor, 32, aead} // AES-256-GCM } -func (e *aesEncryptor) Encrypt(plaintext []byte) ([]byte, error) { +func (e *dataEncryptor) Encrypt(plaintext []byte) ([]byte, error) { key := make([]byte, e.keyLen) if _, err := io.ReadFull(rand.Reader, key); err != nil { return nil, err @@ -132,21 +145,17 @@ func (e *aesEncryptor) Encrypt(plaintext []byte) ([]byte, error) { if err != nil { return nil, err } - block, err := aes.NewCipher(key) + aead, err := e.aead(key) if err != nil { return nil, err } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - nonce := make([]byte, aesgcm.NonceSize()) + nonce := make([]byte, aead.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, err } headerSize := 3 + len(cipherkey) + len(nonce) - buf := make([]byte, headerSize+len(plaintext)+aesgcm.Overhead()) + buf := make([]byte, headerSize+len(plaintext)+aead.Overhead()) buf[0] = byte(len(cipherkey) >> 8) buf[1] = byte(len(cipherkey) & 0xFF) buf[2] = byte(len(nonce)) @@ -155,11 +164,11 @@ func (e *aesEncryptor) Encrypt(plaintext []byte) ([]byte, error) { p = p[len(cipherkey):] copy(p, nonce) p = p[len(nonce):] - ciphertext := aesgcm.Seal(p[:0], nonce, plaintext, nil) + ciphertext := aead.Seal(p[:0], nonce, plaintext, nil) return buf[:headerSize+len(ciphertext)], nil } -func (e *aesEncryptor) Decrypt(ciphertext []byte) ([]byte, error) { +func (e *dataEncryptor) Decrypt(ciphertext []byte) ([]byte, error) { keyLen := int(ciphertext[0])<<8 + int(ciphertext[1]) nonceLen := int(ciphertext[2]) if 3+keyLen+nonceLen >= len(ciphertext) { @@ -174,15 +183,11 @@ func (e *aesEncryptor) Decrypt(ciphertext []byte) ([]byte, error) { if err != nil { return nil, errors.New("decryt key: " + err.Error()) } - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - aesgcm, err := cipher.NewGCM(block) + aead, err := e.aead(key) if err != nil { return nil, err } - return aesgcm.Open(ciphertext[:0], nonce, ciphertext, nil) + return aead.Open(ciphertext[:0], nonce, ciphertext, nil) } type encrypted struct { diff --git a/pkg/object/encrypt_test.go b/pkg/object/encrypt_test.go index 81c53c660cd2..4bcc92ce6d70 100644 --- a/pkg/object/encrypt_test.go +++ b/pkg/object/encrypt_test.go @@ -126,7 +126,7 @@ func BenchmarkRSA4096Decrypt(b *testing.B) { func TestAESGCM(t *testing.T) { kc := NewRSAEncryptor(testkey) - dc := NewAESEncryptor(kc) + dc := NewDataEncryptor(kc, false) data := []byte("hello") ciphertext, _ := dc.Encrypt(data) plaintext, _ := dc.Decrypt(ciphertext) @@ -139,7 +139,7 @@ func TestAESGCM(t *testing.T) { func TestEncryptedStore(t *testing.T) { s, _ := CreateStorage("mem", "", "", "", "") kc := NewRSAEncryptor(testkey) - dc := NewAESEncryptor(kc) + dc := NewDataEncryptor(kc, false) es := NewEncrypted(s, dc) _ = es.Put("a", bytes.NewReader([]byte("hello"))) r, err := es.Get("a", 1, 2) diff --git a/pkg/object/object_storage_test.go b/pkg/object/object_storage_test.go index 54903ce9cef3..4c6b47dad158 100644 --- a/pkg/object/object_storage_test.go +++ b/pkg/object/object_storage_test.go @@ -527,7 +527,7 @@ func TestEncrypted(t *testing.T) { s, _ := CreateStorage("mem", "", "", "", "") privkey, _ := rsa.GenerateKey(rand.Reader, 2048) kc := NewRSAEncryptor(privkey) - dc := NewAESEncryptor(kc) + dc := NewDataEncryptor(kc, false) es := NewEncrypted(s, dc) testStorage(t, es) }