2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-10-25 21:28:16 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package encryption
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/hmac"
|
|
|
|
"crypto/sha512"
|
|
|
|
"encoding/base64"
|
|
|
|
|
|
|
|
"storj.io/storj/pkg/storj"
|
|
|
|
)
|
|
|
|
|
|
|
|
// EncryptPath encrypts path with the given key
|
2019-07-03 19:07:44 +01:00
|
|
|
func EncryptPath(path storj.Path, cipher storj.CipherSuite, key *storj.Key) (encrypted storj.Path, err error) {
|
2018-10-25 21:28:16 +01:00
|
|
|
// do not encrypt empty paths
|
|
|
|
if len(path) == 0 {
|
|
|
|
return path, nil
|
|
|
|
}
|
|
|
|
|
2019-07-03 19:07:44 +01:00
|
|
|
if cipher == storj.EncNull {
|
2018-11-13 12:21:52 +00:00
|
|
|
return path, nil
|
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
comps := storj.SplitPath(path)
|
|
|
|
for i, comp := range comps {
|
2018-11-13 12:21:52 +00:00
|
|
|
comps[i], err = encryptPathComponent(comp, cipher, key)
|
2018-10-25 21:28:16 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
key, err = DeriveKey(key, "path:"+comp)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return storj.JoinPaths(comps...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DecryptPath decrypts path with the given key
|
2019-07-03 19:07:44 +01:00
|
|
|
func DecryptPath(path storj.Path, cipher storj.CipherSuite, key *storj.Key) (decrypted storj.Path, err error) {
|
|
|
|
if cipher == storj.EncNull {
|
2018-11-13 12:21:52 +00:00
|
|
|
return path, nil
|
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
comps := storj.SplitPath(path)
|
|
|
|
for i, comp := range comps {
|
2018-11-13 12:21:52 +00:00
|
|
|
comps[i], err = decryptPathComponent(comp, cipher, key)
|
2018-10-25 21:28:16 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
key, err = DeriveKey(key, "path:"+comps[i])
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return storj.JoinPaths(comps...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DerivePathKey derives the key for the given depth from the given root key.
|
|
|
|
// This method must be called on an unencrypted path.
|
|
|
|
func DerivePathKey(path storj.Path, key *storj.Key, depth int) (derivedKey *storj.Key, err error) {
|
|
|
|
if depth < 0 {
|
|
|
|
return nil, Error.New("negative depth")
|
|
|
|
}
|
|
|
|
|
|
|
|
// do not derive key from empty path
|
|
|
|
if len(path) == 0 {
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
comps := storj.SplitPath(path)
|
|
|
|
if depth > len(comps) {
|
|
|
|
return nil, Error.New("depth greater than path length")
|
|
|
|
}
|
|
|
|
|
|
|
|
derivedKey = key
|
|
|
|
for i := 0; i < depth; i++ {
|
|
|
|
derivedKey, err = DeriveKey(derivedKey, "path:"+comps[i])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return derivedKey, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeriveContentKey derives the key for the encrypted object data using the root key.
|
|
|
|
// This method must be called on an unencrypted path.
|
|
|
|
func DeriveContentKey(path storj.Path, key *storj.Key) (derivedKey *storj.Key, err error) {
|
|
|
|
comps := storj.SplitPath(path)
|
|
|
|
if len(comps) == 0 {
|
|
|
|
return nil, Error.New("path is empty")
|
|
|
|
}
|
|
|
|
derivedKey, err = DerivePathKey(path, key, len(comps))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
derivedKey, err = DeriveKey(derivedKey, "content")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return derivedKey, nil
|
|
|
|
}
|
|
|
|
|
2019-07-03 19:07:44 +01:00
|
|
|
func encryptPathComponent(comp string, cipher storj.CipherSuite, key *storj.Key) (string, error) {
|
2018-10-25 21:28:16 +01:00
|
|
|
// derive the key for the current path component
|
|
|
|
derivedKey, err := DeriveKey(key, "path:"+comp)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// use the derived key to derive the nonce
|
|
|
|
mac := hmac.New(sha512.New, derivedKey[:])
|
|
|
|
_, err = mac.Write([]byte("nonce"))
|
|
|
|
if err != nil {
|
|
|
|
return "", Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
nonce := new(storj.Nonce)
|
2018-10-25 21:28:16 +01:00
|
|
|
copy(nonce[:], mac.Sum(nil))
|
|
|
|
|
|
|
|
// encrypt the path components with the parent's key and the derived nonce
|
2018-11-13 12:21:52 +00:00
|
|
|
cipherText, err := Encrypt([]byte(comp), cipher, key, nonce)
|
2018-10-25 21:28:16 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
nonceSize := storj.NonceSize
|
2019-07-03 19:07:44 +01:00
|
|
|
if cipher == storj.EncAESGCM {
|
2018-11-13 12:21:52 +00:00
|
|
|
nonceSize = AESGCMNonceSize
|
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
// keep the nonce together with the cipher text
|
2018-11-13 12:21:52 +00:00
|
|
|
return base64.RawURLEncoding.EncodeToString(append(nonce[:nonceSize], cipherText...)), nil
|
2018-10-25 21:28:16 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 19:07:44 +01:00
|
|
|
func decryptPathComponent(comp string, cipher storj.CipherSuite, key *storj.Key) (string, error) {
|
2018-10-25 21:28:16 +01:00
|
|
|
if comp == "" {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := base64.RawURLEncoding.DecodeString(comp)
|
|
|
|
if err != nil {
|
|
|
|
return "", Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
nonceSize := storj.NonceSize
|
2019-07-03 19:07:44 +01:00
|
|
|
if cipher == storj.EncAESGCM {
|
2018-11-13 12:21:52 +00:00
|
|
|
nonceSize = AESGCMNonceSize
|
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
// extract the nonce from the cipher text
|
2018-11-13 12:21:52 +00:00
|
|
|
nonce := new(storj.Nonce)
|
|
|
|
copy(nonce[:], data[:nonceSize])
|
2018-10-25 21:28:16 +01:00
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
decrypted, err := Decrypt(data[nonceSize:], cipher, key, nonce)
|
2018-10-25 21:28:16 +01:00
|
|
|
if err != nil {
|
|
|
|
return "", Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(decrypted), nil
|
|
|
|
}
|