mobile: add a way to get an encryption access at some path root (#2519)
* mobile: add a way to get an encryption access at some path root this exposes a way to have keys deeper than the bucket root * APIKey + Caveat exported * use safeError * return nil
This commit is contained in:
parent
10547cc1ea
commit
94eeb58b45
103
mobile/apikey.go
Normal file
103
mobile/apikey.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (C) 2019 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package mobile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
libuplink "storj.io/storj/lib/uplink"
|
||||||
|
"storj.io/storj/pkg/macaroon"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Caveat TODO
|
||||||
|
type Caveat struct {
|
||||||
|
DisallowReads bool
|
||||||
|
DisallowWrites bool
|
||||||
|
DisallowLists bool
|
||||||
|
DisallowDeletes bool
|
||||||
|
AllowedPaths []*CaveatPath
|
||||||
|
// if set, the validity time window
|
||||||
|
NotAfter int64
|
||||||
|
NotBefore int64
|
||||||
|
// nonce is set to some random bytes so that you can make arbitrarily
|
||||||
|
// many restricted macaroons with the same (or no) restrictions.
|
||||||
|
Nonce []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// CaveatPath If any entries exist, require all access to happen in at least
|
||||||
|
// one of them.
|
||||||
|
type CaveatPath struct {
|
||||||
|
Bucket []byte
|
||||||
|
EncryptedPathPrefix []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCaveat TODO
|
||||||
|
func NewCaveat() *Caveat {
|
||||||
|
return &Caveat{
|
||||||
|
AllowedPaths: make([]*CaveatPath, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCaveatPath TODO
|
||||||
|
func (caveat Caveat) AddCaveatPath(path *CaveatPath) {
|
||||||
|
caveat.AllowedPaths = append(caveat.AllowedPaths, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIKey represents an access credential to certain resources
|
||||||
|
type APIKey struct {
|
||||||
|
lib *libuplink.APIKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize serializes the API key to a string
|
||||||
|
func (a APIKey) Serialize() string {
|
||||||
|
return a.lib.Serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns if the api key is an uninitialized value
|
||||||
|
func (a *APIKey) IsZero() bool {
|
||||||
|
return a.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAPIKey parses an API key
|
||||||
|
func ParseAPIKey(val string) (*APIKey, error) {
|
||||||
|
k, err := libuplink.ParseAPIKey(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, safeError(err)
|
||||||
|
}
|
||||||
|
return &APIKey{lib: &k}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restrict generates a new APIKey with the provided Caveat attached.
|
||||||
|
func (a APIKey) Restrict(caveat *Caveat) (*APIKey, error) {
|
||||||
|
paths := make([]*macaroon.Caveat_Path, 0)
|
||||||
|
for _, path := range caveat.AllowedPaths {
|
||||||
|
paths = append(paths, &macaroon.Caveat_Path{
|
||||||
|
Bucket: path.Bucket,
|
||||||
|
EncryptedPathPrefix: path.EncryptedPathPrefix,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
libCaveat := macaroon.Caveat{
|
||||||
|
DisallowReads: caveat.DisallowReads,
|
||||||
|
DisallowWrites: caveat.DisallowWrites,
|
||||||
|
DisallowLists: caveat.DisallowLists,
|
||||||
|
DisallowDeletes: caveat.DisallowDeletes,
|
||||||
|
AllowedPaths: paths,
|
||||||
|
Nonce: caveat.Nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
if caveat.NotAfter != 0 {
|
||||||
|
notAfter := time.Unix(caveat.NotAfter, 0)
|
||||||
|
libCaveat.NotAfter = ¬After
|
||||||
|
}
|
||||||
|
if caveat.NotBefore != 0 {
|
||||||
|
notBefore := time.Unix(caveat.NotBefore, 0)
|
||||||
|
libCaveat.NotBefore = ¬Before
|
||||||
|
}
|
||||||
|
|
||||||
|
k, err := a.lib.Restrict(libCaveat)
|
||||||
|
if err != nil {
|
||||||
|
return nil, safeError(err)
|
||||||
|
}
|
||||||
|
return &APIKey{lib: &k}, nil
|
||||||
|
}
|
@ -5,6 +5,7 @@ package mobile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
libuplink "storj.io/storj/lib/uplink"
|
libuplink "storj.io/storj/lib/uplink"
|
||||||
|
"storj.io/storj/pkg/paths"
|
||||||
"storj.io/storj/pkg/storj"
|
"storj.io/storj/pkg/storj"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,6 +19,20 @@ func NewEncryptionAccess() *EncryptionAccess {
|
|||||||
return &EncryptionAccess{lib: libuplink.NewEncryptionAccess()}
|
return &EncryptionAccess{lib: libuplink.NewEncryptionAccess()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewEncryptionAccessWithRoot constructs an encryption access with a key rooted at the provided path inside of a bucket.
|
||||||
|
func NewEncryptionAccessWithRoot(bucket, unencryptedPath, encryptedPath string, keyData []byte) (*EncryptionAccess, error) {
|
||||||
|
key, err := storj.NewKey(keyData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, safeError(err)
|
||||||
|
}
|
||||||
|
encAccess := libuplink.NewEncryptionAccess()
|
||||||
|
err = encAccess.Store().Add(bucket, paths.NewUnencrypted(unencryptedPath), paths.NewEncrypted(encryptedPath), *key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, safeError(err)
|
||||||
|
}
|
||||||
|
return &EncryptionAccess{lib: encAccess}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetDefaultKey sets the default key to use when no matching keys are found
|
// SetDefaultKey sets the default key to use when no matching keys are found
|
||||||
// for the encryption context.
|
// for the encryption context.
|
||||||
func (e *EncryptionAccess) SetDefaultKey(keyData []byte) error {
|
func (e *EncryptionAccess) SetDefaultKey(keyData []byte) error {
|
||||||
|
@ -338,4 +338,51 @@ public class LibuplinkInstrumentedTest {
|
|||||||
uplink.close();
|
uplink.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptionAccessWithRoot() throws Exception {
|
||||||
|
Config config = new Config();
|
||||||
|
|
||||||
|
Uplink uplink = new Uplink(config, filesDir);
|
||||||
|
try {
|
||||||
|
Project project = uplink.openProject(VALID_SATELLITE_ADDRESS, VALID_API_KEY);
|
||||||
|
try {
|
||||||
|
byte[] saltedKey = project.saltedKeyFromPassphrase("some-passphrase");
|
||||||
|
EncryptionAccess ea = Mobile.newEncryptionAccessWithRoot("bucket", "unencryptedPath", "encryptedPath", saltedKey);
|
||||||
|
String serialized = ea.serialize();
|
||||||
|
assertNotEquals("", serialized);
|
||||||
|
} finally {
|
||||||
|
project.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
uplink.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApiKey() throws Exception {
|
||||||
|
String apiKeyData = "13YqeKQiA3ANSuDu4rqX6eGs3YWox9GRi9rEUKy1HidXiNNm6a5SiE49Hk9gomHZVcQhq4eFQh8yhDgfGKg268j6vqWKEhnJjFPLqAP";
|
||||||
|
APIKey apiKey = Mobile.parseAPIKey(apiKeyData);
|
||||||
|
|
||||||
|
String serialized = apiKey.serialize();
|
||||||
|
assertEquals(serialized, apiKeyData);
|
||||||
|
|
||||||
|
Caveat caveat = new Caveat();
|
||||||
|
caveat.setDisallowDeletes(true);
|
||||||
|
caveat.setDisallowWrites(true);
|
||||||
|
caveat.setDisallowReads(true);
|
||||||
|
caveat.setDisallowLists(true);
|
||||||
|
caveat.setNotAfter(100);
|
||||||
|
caveat.setNotBefore(50);
|
||||||
|
|
||||||
|
CaveatPath path = new CaveatPath();
|
||||||
|
path.setBucket("bucket".getBytes());
|
||||||
|
path.setEncryptedPathPrefix("123456".getBytes());
|
||||||
|
caveat.addCaveatPath(path);
|
||||||
|
|
||||||
|
APIKey newAPIKey = apiKey.restrict(caveat);
|
||||||
|
assertNotEquals("", newAPIKey.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user