Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/object: support chacha20 cipher #2330

Merged
merged 3 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion cmd/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ Details: https://juicefs.com/docs/community/quick_start_guide`,
Name: "encrypt-rsa-key",
Usage: "a path to RSA private key (PEM)",
},
&cli.StringFlag{
Name: "encrypt-algo",
Usage: "encrypt algorithm (aes256gcm-rsa, chacha20-rsa)",
Value: object.AES256GCM_RSA,
},
&cli.IntFlag{
Name: "trash-days",
Value: 1,
Expand Down Expand Up @@ -237,7 +242,10 @@ 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, err := object.NewDataEncryptor(object.NewRSAEncryptor(privKey), format.EncryptAglo)
if err != nil {
return nil, err
}
blob = object.NewEncrypted(blob, encryptor)
}
return blob, nil
Expand Down Expand Up @@ -392,6 +400,7 @@ func format(c *cli.Context) error {
SecretKey: c.String("secret-key"),
SessionToken: c.String("session-token"),
EncryptKey: loadEncrypt(c.String("encrypt-rsa-key")),
EncryptAglo: c.String("encrypt-aglo"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flag should be ignored like encrypt-rsa-key as handled in L388~L389

Shards: c.Int("shards"),
HashPrefix: c.Bool("hash-prefix"),
Capacity: c.Uint64("capacity") << 30,
Expand Down
1 change: 1 addition & 0 deletions pkg/meta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type Format struct {
Capacity uint64 `json:",omitempty"`
Inodes uint64 `json:",omitempty"`
EncryptKey string `json:",omitempty"`
EncryptAglo string `json:",omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be EncryptAlgo

KeyEncrypted bool `json:",omitempty"`
TrashDays int `json:",omitempty"`
MetaVersion int `json:",omitempty"`
Expand Down
51 changes: 32 additions & 19 deletions pkg/object/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import (
"io"
"io/ioutil"
"strings"

"golang.org/x/crypto/chacha20poly1305"
)

type Encryptor interface {
Expand Down Expand Up @@ -114,16 +116,35 @@ 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
const (
AES256GCM_RSA = "aes256gcm-rsa"
CHACHA20_RSA = "chacha20-rsa"
)

func NewDataEncryptor(keyEncryptor Encryptor, algo string) (Encryptor, error) {
switch algo {
case "", AES256GCM_RSA:
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}, nil
case CHACHA20_RSA:
return &dataEncryptor{keyEncryptor, chacha20poly1305.KeySize, chacha20poly1305.New}, nil
}
return nil, fmt.Errorf("unsupport cipher: %s", algo)
}

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
Expand All @@ -132,21 +153,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))
Expand All @@ -155,11 +172,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) {
Expand All @@ -174,15 +191,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 {
Expand Down
4 changes: 2 additions & 2 deletions pkg/object/encrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func BenchmarkRSA4096Decrypt(b *testing.B) {

func TestAESGCM(t *testing.T) {
kc := NewRSAEncryptor(testkey)
dc := NewAESEncryptor(kc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 2 return values

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add tests for ChaCha20?

dc := NewDataEncryptor(kc, AES256GCM_RSA)
data := []byte("hello")
ciphertext, _ := dc.Encrypt(data)
plaintext, _ := dc.Decrypt(ciphertext)
Expand All @@ -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, AES256GCM_RSA)
es := NewEncrypted(s, dc)
_ = es.Put("a", bytes.NewReader([]byte("hello")))
r, err := es.Get("a", 1, 2)
Expand Down
2 changes: 1 addition & 1 deletion pkg/object/object_storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, AES256GCM_RSA)
es := NewEncrypted(s, dc)
testStorage(t, es)
}
Expand Down