cmd/swarm: added password to ACT (#17598)

This commit is contained in:
Elad 2018-09-07 09:56:05 +02:00 committed by Balint Gabor
parent 580145e96d
commit 70d31fb278
5 changed files with 250 additions and 169 deletions

View File

@ -51,7 +51,7 @@ func accessNewPass(ctx *cli.Context) {
password = getPassPhrase("", 0, makePasswordList(ctx)) password = getPassPhrase("", 0, makePasswordList(ctx))
dryRun = ctx.Bool(SwarmDryRunFlag.Name) dryRun = ctx.Bool(SwarmDryRunFlag.Name)
) )
accessKey, ae, err = api.DoPasswordNew(ctx, password, salt) accessKey, ae, err = api.DoPassword(ctx, password, salt)
if err != nil { if err != nil {
utils.Fatalf("error getting session key: %v", err) utils.Fatalf("error getting session key: %v", err)
} }
@ -85,7 +85,7 @@ func accessNewPK(ctx *cli.Context) {
granteePublicKey = ctx.String(SwarmAccessGrantKeyFlag.Name) granteePublicKey = ctx.String(SwarmAccessGrantKeyFlag.Name)
dryRun = ctx.Bool(SwarmDryRunFlag.Name) dryRun = ctx.Bool(SwarmDryRunFlag.Name)
) )
sessionKey, ae, err = api.DoPKNew(ctx, privateKey, granteePublicKey, salt) sessionKey, ae, err = api.DoPK(ctx, privateKey, granteePublicKey, salt)
if err != nil { if err != nil {
utils.Fatalf("error getting session key: %v", err) utils.Fatalf("error getting session key: %v", err)
} }
@ -110,23 +110,38 @@ func accessNewACT(ctx *cli.Context) {
} }
var ( var (
ae *api.AccessEntry ae *api.AccessEntry
actManifest *api.Manifest actManifest *api.Manifest
accessKey []byte accessKey []byte
err error err error
ref = args[0] ref = args[0]
grantees = []string{} pkGrantees = []string{}
actFilename = ctx.String(SwarmAccessGrantKeysFlag.Name) passGrantees = []string{}
privateKey = getPrivKey(ctx) pkGranteesFilename = ctx.String(SwarmAccessGrantKeysFlag.Name)
dryRun = ctx.Bool(SwarmDryRunFlag.Name) passGranteesFilename = ctx.String(utils.PasswordFileFlag.Name)
privateKey = getPrivKey(ctx)
dryRun = ctx.Bool(SwarmDryRunFlag.Name)
) )
if pkGranteesFilename == "" && passGranteesFilename == "" {
bytes, err := ioutil.ReadFile(actFilename) utils.Fatalf("you have to provide either a grantee public-keys file or an encryption passwords file (or both)")
if err != nil {
utils.Fatalf("had an error reading the grantee public key list")
} }
grantees = strings.Split(string(bytes), "\n")
accessKey, ae, actManifest, err = api.DoACTNew(ctx, privateKey, salt, grantees) if pkGranteesFilename != "" {
bytes, err := ioutil.ReadFile(pkGranteesFilename)
if err != nil {
utils.Fatalf("had an error reading the grantee public key list")
}
pkGrantees = strings.Split(string(bytes), "\n")
}
if passGranteesFilename != "" {
bytes, err := ioutil.ReadFile(passGranteesFilename)
if err != nil {
utils.Fatalf("could not read password filename: %v", err)
}
passGrantees = strings.Split(string(bytes), "\n")
}
accessKey, ae, actManifest, err = api.DoACT(ctx, privateKey, salt, pkGrantees, passGrantees)
if err != nil { if err != nil {
utils.Fatalf("error generating ACT manifest: %v", err) utils.Fatalf("error generating ACT manifest: %v", err)
} }

View File

@ -28,7 +28,6 @@ import (
gorand "math/rand" gorand "math/rand"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -39,6 +38,12 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/api"
swarm "github.com/ethereum/go-ethereum/swarm/api/client" swarm "github.com/ethereum/go-ethereum/swarm/api/client"
"github.com/ethereum/go-ethereum/swarm/testutil"
)
const (
hashRegexp = `[a-f\d]{128}`
data = "notsorandomdata"
) )
var DefaultCurve = crypto.S256() var DefaultCurve = crypto.S256()
@ -53,23 +58,8 @@ func TestAccessPassword(t *testing.T) {
defer cluster.Shutdown() defer cluster.Shutdown()
proxyNode := cluster.Nodes[0] proxyNode := cluster.Nodes[0]
// create a tmp file dataFilename := testutil.TempFileWithContent(t, data)
tmp, err := ioutil.TempDir("", "swarm-test") defer os.RemoveAll(dataFilename)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
// write data to file
data := "notsorandomdata"
dataFilename := filepath.Join(tmp, "data.txt")
err = ioutil.WriteFile(dataFilename, []byte(data), 0666)
if err != nil {
t.Fatal(err)
}
hashRegexp := `[a-f\d]{128}`
// upload the file with 'swarm up' and expect a hash // upload the file with 'swarm up' and expect a hash
up := runSwarm(t, up := runSwarm(t,
@ -86,14 +76,14 @@ func TestAccessPassword(t *testing.T) {
} }
ref := matches[0] ref := matches[0]
tmp, err := ioutil.TempDir("", "swarm-test")
password := "smth"
passwordFilename := filepath.Join(tmp, "password.txt")
err = ioutil.WriteFile(passwordFilename, []byte(password), 0666)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer os.RemoveAll(tmp)
password := "smth"
passwordFilename := testutil.TempFileWithContent(t, "smth")
defer os.RemoveAll(passwordFilename)
up = runSwarm(t, up = runSwarm(t,
"access", "access",
@ -193,12 +183,8 @@ func TestAccessPassword(t *testing.T) {
t.Errorf("expected decrypted data %q, got %q", data, string(d)) t.Errorf("expected decrypted data %q, got %q", data, string(d))
} }
wrongPasswordFilename := filepath.Join(tmp, "password-wrong.txt") wrongPasswordFilename := testutil.TempFileWithContent(t, "just wr0ng")
defer os.RemoveAll(wrongPasswordFilename)
err = ioutil.WriteFile(wrongPasswordFilename, []byte("just wr0ng"), 0666)
if err != nil {
t.Fatal(err)
}
//download file with 'swarm down' with wrong password //download file with 'swarm down' with wrong password
up = runSwarm(t, up = runSwarm(t,
@ -227,22 +213,8 @@ func TestAccessPK(t *testing.T) {
cluster := newTestCluster(t, 2) cluster := newTestCluster(t, 2)
defer cluster.Shutdown() defer cluster.Shutdown()
// create a tmp file dataFilename := testutil.TempFileWithContent(t, data)
tmp, err := ioutil.TempFile("", "swarm-test") defer os.RemoveAll(dataFilename)
if err != nil {
t.Fatal(err)
}
defer tmp.Close()
defer os.Remove(tmp.Name())
// write data to file
data := "notsorandomdata"
_, err = io.WriteString(tmp, data)
if err != nil {
t.Fatal(err)
}
hashRegexp := `[a-f\d]{128}`
// upload the file with 'swarm up' and expect a hash // upload the file with 'swarm up' and expect a hash
up := runSwarm(t, up := runSwarm(t,
@ -250,7 +222,7 @@ func TestAccessPK(t *testing.T) {
cluster.Nodes[0].URL, cluster.Nodes[0].URL,
"up", "up",
"--encrypt", "--encrypt",
tmp.Name()) dataFilename)
_, matches := up.ExpectRegexp(hashRegexp) _, matches := up.ExpectRegexp(hashRegexp)
up.ExpectExit() up.ExpectExit()
@ -259,7 +231,6 @@ func TestAccessPK(t *testing.T) {
} }
ref := matches[0] ref := matches[0]
pk := cluster.Nodes[0].PrivateKey pk := cluster.Nodes[0].PrivateKey
granteePubKey := crypto.CompressPubkey(&pk.PublicKey) granteePubKey := crypto.CompressPubkey(&pk.PublicKey)
@ -268,22 +239,15 @@ func TestAccessPK(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
passFile, err := ioutil.TempFile("", "swarm-test") passwordFilename := testutil.TempFileWithContent(t, testPassphrase)
if err != nil { defer os.RemoveAll(passwordFilename)
t.Fatal(err)
}
defer passFile.Close()
defer os.Remove(passFile.Name())
_, err = io.WriteString(passFile, testPassphrase)
if err != nil {
t.Fatal(err)
}
_, publisherAccount := getTestAccount(t, publisherDir) _, publisherAccount := getTestAccount(t, publisherDir)
up = runSwarm(t, up = runSwarm(t,
"--bzzaccount", "--bzzaccount",
publisherAccount.Address.String(), publisherAccount.Address.String(),
"--password", "--password",
passFile.Name(), passwordFilename,
"--datadir", "--datadir",
publisherDir, publisherDir,
"--bzzapi", "--bzzapi",
@ -309,7 +273,7 @@ func TestAccessPK(t *testing.T) {
"--bzzaccount", "--bzzaccount",
publisherAccount.Address.String(), publisherAccount.Address.String(),
"--password", "--password",
passFile.Name(), passwordFilename,
"--datadir", "--datadir",
publisherDir, publisherDir,
"print-keys", "print-keys",
@ -390,37 +354,24 @@ func TestAccessACTScale(t *testing.T) {
testAccessACT(t, 1000) testAccessACT(t, 1000)
} }
// TestAccessACT tests the e2e creation, uploading and downloading of an ACT type access control // TestAccessACT tests the e2e creation, uploading and downloading of an ACT access control with both EC keys AND password protection
// the test fires up a 3 node cluster, then randomly picks 2 nodes which will be acting as grantees to the data // the test fires up a 3 node cluster, then randomly picks 2 nodes which will be acting as grantees to the data
// set. the third node should fail decoding the reference as it will not be granted access. the publisher uploads through // set and also protects the ACT with a password. the third node should fail decoding the reference as it will not be granted access.
// one of the nodes then disappears. If `bogusEntries` is bigger than 0, the test will generate the number of bogus act entries // the third node then then tries to download using a correct password (and succeeds) then uses a wrong password and fails.
// to test what happens at scale // the publisher uploads through one of the nodes then disappears.
func testAccessACT(t *testing.T, bogusEntries int) { func testAccessACT(t *testing.T, bogusEntries int) {
// Setup Swarm and upload a test file to it // Setup Swarm and upload a test file to it
cluster := newTestCluster(t, 3) const clusterSize = 3
cluster := newTestCluster(t, clusterSize)
defer cluster.Shutdown() defer cluster.Shutdown()
var uploadThroughNode = cluster.Nodes[0] var uploadThroughNode = cluster.Nodes[0]
client := swarm.NewClient(uploadThroughNode.URL) client := swarm.NewClient(uploadThroughNode.URL)
r1 := gorand.New(gorand.NewSource(time.Now().UnixNano())) r1 := gorand.New(gorand.NewSource(time.Now().UnixNano()))
nodeToSkip := r1.Intn(3) // a number between 0 and 2 (node indices in `cluster`) nodeToSkip := r1.Intn(clusterSize) // a number between 0 and 2 (node indices in `cluster`)
// create a tmp file dataFilename := testutil.TempFileWithContent(t, data)
tmp, err := ioutil.TempFile("", "swarm-test") defer os.RemoveAll(dataFilename)
if err != nil {
t.Fatal(err)
}
defer tmp.Close()
defer os.Remove(tmp.Name())
// write data to file
data := "notsorandomdata"
_, err = io.WriteString(tmp, data)
if err != nil {
t.Fatal(err)
}
hashRegexp := `[a-f\d]{128}`
// upload the file with 'swarm up' and expect a hash // upload the file with 'swarm up' and expect a hash
up := runSwarm(t, up := runSwarm(t,
@ -428,7 +379,7 @@ func testAccessACT(t *testing.T, bogusEntries int) {
cluster.Nodes[0].URL, cluster.Nodes[0].URL,
"up", "up",
"--encrypt", "--encrypt",
tmp.Name()) dataFilename)
_, matches := up.ExpectRegexp(hashRegexp) _, matches := up.ExpectRegexp(hashRegexp)
up.ExpectExit() up.ExpectExit()
@ -464,41 +415,25 @@ func testAccessACT(t *testing.T, bogusEntries int) {
} }
grantees = bogusGrantees grantees = bogusGrantees
} }
granteesPubkeyListFile := testutil.TempFileWithContent(t, strings.Join(grantees, "\n"))
granteesPubkeyListFile, err := ioutil.TempFile("", "grantees-pubkey-list") defer os.RemoveAll(granteesPubkeyListFile)
if err != nil {
t.Fatal(err)
}
defer granteesPubkeyListFile.Close()
defer os.Remove(granteesPubkeyListFile.Name())
_, err = granteesPubkeyListFile.WriteString(strings.Join(grantees, "\n"))
if err != nil {
t.Fatal(err)
}
publisherDir, err := ioutil.TempDir("", "swarm-account-dir-temp") publisherDir, err := ioutil.TempDir("", "swarm-account-dir-temp")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer os.RemoveAll(publisherDir)
passFile, err := ioutil.TempFile("", "swarm-test") passwordFilename := testutil.TempFileWithContent(t, testPassphrase)
if err != nil { defer os.RemoveAll(passwordFilename)
t.Fatal(err) actPasswordFilename := testutil.TempFileWithContent(t, "smth")
} defer os.RemoveAll(actPasswordFilename)
defer passFile.Close()
defer os.Remove(passFile.Name())
_, err = io.WriteString(passFile, testPassphrase)
if err != nil {
t.Fatal(err)
}
_, publisherAccount := getTestAccount(t, publisherDir) _, publisherAccount := getTestAccount(t, publisherDir)
up = runSwarm(t, up = runSwarm(t,
"--bzzaccount", "--bzzaccount",
publisherAccount.Address.String(), publisherAccount.Address.String(),
"--password", "--password",
passFile.Name(), passwordFilename,
"--datadir", "--datadir",
publisherDir, publisherDir,
"--bzzapi", "--bzzapi",
@ -507,7 +442,9 @@ func testAccessACT(t *testing.T, bogusEntries int) {
"new", "new",
"act", "act",
"--grant-keys", "--grant-keys",
granteesPubkeyListFile.Name(), granteesPubkeyListFile,
"--password",
actPasswordFilename,
ref, ref,
) )
@ -523,7 +460,7 @@ func testAccessACT(t *testing.T, bogusEntries int) {
"--bzzaccount", "--bzzaccount",
publisherAccount.Address.String(), publisherAccount.Address.String(),
"--password", "--password",
passFile.Name(), passwordFilename,
"--datadir", "--datadir",
publisherDir, publisherDir,
"print-keys", "print-keys",
@ -562,9 +499,7 @@ func testAccessACT(t *testing.T, bogusEntries int) {
if len(a.Salt) < 32 { if len(a.Salt) < 32 {
t.Fatalf(`got salt with length %v, expected not less the 32 bytes`, len(a.Salt)) t.Fatalf(`got salt with length %v, expected not less the 32 bytes`, len(a.Salt))
} }
if a.KdfParams != nil {
t.Fatal("manifest access kdf params should be nil")
}
if a.Publisher != pkComp { if a.Publisher != pkComp {
t.Fatal("publisher key did not match") t.Fatal("publisher key did not match")
} }
@ -588,6 +523,25 @@ func testAccessACT(t *testing.T, bogusEntries int) {
t.Fatalf("should be a 401") t.Fatalf("should be a 401")
} }
// try downloading using a password instead, using the unauthorized node
passwordUrl := strings.Replace(url, "http://", "http://:smth@", -1)
response, err = httpClient.Get(passwordUrl)
if err != nil {
t.Fatal(err)
}
if response.StatusCode != http.StatusOK {
t.Fatal("should be a 200")
}
// now try with the wrong password, expect 401
passwordUrl = strings.Replace(url, "http://", "http://:smthWrong@", -1)
response, err = httpClient.Get(passwordUrl)
if err != nil {
t.Fatal(err)
}
if response.StatusCode != http.StatusUnauthorized {
t.Fatal("should be a 401")
}
continue continue
} }

View File

@ -319,6 +319,7 @@ func init() {
Flags: []cli.Flag{ Flags: []cli.Flag{
SwarmAccessGrantKeysFlag, SwarmAccessGrantKeysFlag,
SwarmDryRunFlag, SwarmDryRunFlag,
utils.PasswordFileFlag,
}, },
Name: "act", Name: "act",
Usage: "encrypts a reference with the node's private key and a given grantee's public key and embeds it into a root manifest", Usage: "encrypts a reference with the node's private key and a given grantee's public key and embeds it into a root manifest",

View File

@ -102,6 +102,7 @@ const AccessTypePass = AccessType("pass")
const AccessTypePK = AccessType("pk") const AccessTypePK = AccessType("pk")
const AccessTypeACT = AccessType("act") const AccessTypeACT = AccessType("act")
// NewAccessEntryPassword creates a manifest AccessEntry in order to create an ACT protected by a password
func NewAccessEntryPassword(salt []byte, kdfParams *KdfParams) (*AccessEntry, error) { func NewAccessEntryPassword(salt []byte, kdfParams *KdfParams) (*AccessEntry, error) {
if len(salt) != 32 { if len(salt) != 32 {
return nil, fmt.Errorf("salt should be 32 bytes long") return nil, fmt.Errorf("salt should be 32 bytes long")
@ -113,6 +114,7 @@ func NewAccessEntryPassword(salt []byte, kdfParams *KdfParams) (*AccessEntry, er
}, nil }, nil
} }
// NewAccessEntryPK creates a manifest AccessEntry in order to create an ACT protected by a pair of Elliptic Curve keys
func NewAccessEntryPK(publisher string, salt []byte) (*AccessEntry, error) { func NewAccessEntryPK(publisher string, salt []byte) (*AccessEntry, error) {
if len(publisher) != 66 { if len(publisher) != 66 {
return nil, fmt.Errorf("publisher should be 66 characters long, got %d", len(publisher)) return nil, fmt.Errorf("publisher should be 66 characters long, got %d", len(publisher))
@ -127,6 +129,7 @@ func NewAccessEntryPK(publisher string, salt []byte) (*AccessEntry, error) {
}, nil }, nil
} }
// NewAccessEntryACT creates a manifest AccessEntry in order to create an ACT protected by a combination of EC keys and passwords
func NewAccessEntryACT(publisher string, salt []byte, act string) (*AccessEntry, error) { func NewAccessEntryACT(publisher string, salt []byte, act string) (*AccessEntry, error) {
if len(salt) != 32 { if len(salt) != 32 {
return nil, fmt.Errorf("salt should be 32 bytes long") return nil, fmt.Errorf("salt should be 32 bytes long")
@ -140,15 +143,19 @@ func NewAccessEntryACT(publisher string, salt []byte, act string) (*AccessEntry,
Publisher: publisher, Publisher: publisher,
Salt: salt, Salt: salt,
Act: act, Act: act,
KdfParams: DefaultKdfParams,
}, nil }, nil
} }
// NOOPDecrypt is a generic decrypt function that is passed into the API in places where real ACT decryption capabilities are
// either unwanted, or alternatively, cannot be implemented in the immediate scope
func NOOPDecrypt(*ManifestEntry) error { func NOOPDecrypt(*ManifestEntry) error {
return nil return nil
} }
var DefaultKdfParams = NewKdfParams(262144, 1, 8) var DefaultKdfParams = NewKdfParams(262144, 1, 8)
// NewKdfParams returns a KdfParams struct with the given scrypt params
func NewKdfParams(n, p, r int) *KdfParams { func NewKdfParams(n, p, r int) *KdfParams {
return &KdfParams{ return &KdfParams{
@ -161,15 +168,20 @@ func NewKdfParams(n, p, r int) *KdfParams {
// NewSessionKeyPassword creates a session key based on a shared secret (password) and the given salt // NewSessionKeyPassword creates a session key based on a shared secret (password) and the given salt
// and kdf parameters in the access entry // and kdf parameters in the access entry
func NewSessionKeyPassword(password string, accessEntry *AccessEntry) ([]byte, error) { func NewSessionKeyPassword(password string, accessEntry *AccessEntry) ([]byte, error) {
if accessEntry.Type != AccessTypePass { if accessEntry.Type != AccessTypePass && accessEntry.Type != AccessTypeACT {
return nil, errors.New("incorrect access entry type") return nil, errors.New("incorrect access entry type")
} }
return sessionKeyPassword(password, accessEntry.Salt, accessEntry.KdfParams)
}
func sessionKeyPassword(password string, salt []byte, kdfParams *KdfParams) ([]byte, error) {
return scrypt.Key( return scrypt.Key(
[]byte(password), []byte(password),
accessEntry.Salt, salt,
accessEntry.KdfParams.N, kdfParams.N,
accessEntry.KdfParams.R, kdfParams.R,
accessEntry.KdfParams.P, kdfParams.P,
32, 32,
) )
} }
@ -188,9 +200,6 @@ func NewSessionKeyPK(private *ecdsa.PrivateKey, public *ecdsa.PublicKey, salt []
return sessionKey, nil return sessionKey, nil
} }
func (a *API) NodeSessionKey(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, salt []byte) ([]byte, error) {
return NewSessionKeyPK(privateKey, publicKey, salt)
}
func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.PrivateKey) DecryptFunc { func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.PrivateKey) DecryptFunc {
return func(m *ManifestEntry) error { return func(m *ManifestEntry) error {
if m.Access == nil { if m.Access == nil {
@ -242,7 +251,7 @@ func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.Priva
if err != nil { if err != nil {
return ErrDecrypt return ErrDecrypt
} }
key, err := a.NodeSessionKey(pk, publisher, m.Access.Salt) key, err := NewSessionKeyPK(pk, publisher, m.Access.Salt)
if err != nil { if err != nil {
return ErrDecrypt return ErrDecrypt
} }
@ -261,6 +270,11 @@ func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.Priva
m.Access = nil m.Access = nil
return nil return nil
case "act": case "act":
var (
sessionKey []byte
err error
)
publisherBytes, err := hex.DecodeString(m.Access.Publisher) publisherBytes, err := hex.DecodeString(m.Access.Publisher)
if err != nil { if err != nil {
return ErrDecrypt return ErrDecrypt
@ -270,40 +284,35 @@ func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.Priva
return ErrDecrypt return ErrDecrypt
} }
sessionKey, err := a.NodeSessionKey(pk, publisher, m.Access.Salt) sessionKey, err = NewSessionKeyPK(pk, publisher, m.Access.Salt)
if err != nil { if err != nil {
return ErrDecrypt return ErrDecrypt
} }
hasher := sha3.NewKeccak256() found, ciphertext, decryptionKey, err := a.getACTDecryptionKey(ctx, storage.Address(common.Hex2Bytes(m.Access.Act)), sessionKey)
hasher.Write(append(sessionKey, 0))
lookupKey := hasher.Sum(nil)
hasher.Reset()
hasher.Write(append(sessionKey, 1))
accessKeyDecryptionKey := hasher.Sum(nil)
lk := hex.EncodeToString(lookupKey)
list, err := a.GetManifestList(ctx, NOOPDecrypt, storage.Address(common.Hex2Bytes(m.Access.Act)), lk)
found := ""
for _, v := range list.Entries {
if v.Path == lk {
found = v.Hash
}
}
if found == "" {
return ErrDecrypt
}
v, err := hex.DecodeString(found)
if err != nil { if err != nil {
return err return err
} }
enc := NewRefEncryption(len(v) - 8) if !found {
decodedRef, err := enc.Decrypt(v, accessKeyDecryptionKey) // try to fall back to password
if credentials != "" {
sessionKey, err = NewSessionKeyPassword(credentials, m.Access)
if err != nil {
return err
}
found, ciphertext, decryptionKey, err = a.getACTDecryptionKey(ctx, storage.Address(common.Hex2Bytes(m.Access.Act)), sessionKey)
if err != nil {
return err
}
if !found {
return ErrDecrypt
}
} else {
return ErrDecrypt
}
}
enc := NewRefEncryption(len(ciphertext) - 8)
decodedRef, err := enc.Decrypt(ciphertext, decryptionKey)
if err != nil { if err != nil {
return ErrDecrypt return ErrDecrypt
} }
@ -326,6 +335,33 @@ func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.Priva
} }
} }
func (a *API) getACTDecryptionKey(ctx context.Context, actManifestAddress storage.Address, sessionKey []byte) (found bool, ciphertext, decryptionKey []byte, err error) {
hasher := sha3.NewKeccak256()
hasher.Write(append(sessionKey, 0))
lookupKey := hasher.Sum(nil)
hasher.Reset()
hasher.Write(append(sessionKey, 1))
accessKeyDecryptionKey := hasher.Sum(nil)
hasher.Reset()
lk := hex.EncodeToString(lookupKey)
list, err := a.GetManifestList(ctx, NOOPDecrypt, actManifestAddress, lk)
if err != nil {
return false, nil, nil, err
}
for _, v := range list.Entries {
if v.Path == lk {
cipherTextBytes, err := hex.DecodeString(v.Hash)
if err != nil {
return false, nil, nil, err
}
return true, cipherTextBytes, accessKeyDecryptionKey, nil
}
}
return false, nil, nil, nil
}
func GenerateAccessControlManifest(ctx *cli.Context, ref string, accessKey []byte, ae *AccessEntry) (*Manifest, error) { func GenerateAccessControlManifest(ctx *cli.Context, ref string, accessKey []byte, ae *AccessEntry) (*Manifest, error) {
refBytes, err := hex.DecodeString(ref) refBytes, err := hex.DecodeString(ref)
if err != nil { if err != nil {
@ -352,7 +388,9 @@ func GenerateAccessControlManifest(ctx *cli.Context, ref string, accessKey []byt
return m, nil return m, nil
} }
func DoPKNew(ctx *cli.Context, privateKey *ecdsa.PrivateKey, granteePublicKey string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) { // DoPK is a helper function to the CLI API that handles the entire business logic for
// creating a session key and access entry given the cli context, ec keys and salt
func DoPK(ctx *cli.Context, privateKey *ecdsa.PrivateKey, granteePublicKey string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) {
if granteePublicKey == "" { if granteePublicKey == "" {
return nil, nil, errors.New("need a grantee Public Key") return nil, nil, errors.New("need a grantee Public Key")
} }
@ -383,9 +421,11 @@ func DoPKNew(ctx *cli.Context, privateKey *ecdsa.PrivateKey, granteePublicKey st
return sessionKey, ae, nil return sessionKey, ae, nil
} }
func DoACTNew(ctx *cli.Context, privateKey *ecdsa.PrivateKey, salt []byte, grantees []string) (accessKey []byte, ae *AccessEntry, actManifest *Manifest, err error) { // DoACT is a helper function to the CLI API that handles the entire business logic for
if len(grantees) == 0 { // creating a access key, access entry and ACT manifest (including uploading it) given the cli context, ec keys, password grantees and salt
return nil, nil, nil, errors.New("did not get any grantee public keys") func DoACT(ctx *cli.Context, privateKey *ecdsa.PrivateKey, salt []byte, grantees []string, encryptPasswords []string) (accessKey []byte, ae *AccessEntry, actManifest *Manifest, err error) {
if len(grantees) == 0 && len(encryptPasswords) == 0 {
return nil, nil, nil, errors.New("did not get any grantee public keys or any encryption passwords")
} }
publisherPub := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)) publisherPub := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
@ -430,7 +470,31 @@ func DoACTNew(ctx *cli.Context, privateKey *ecdsa.PrivateKey, salt []byte, grant
enc := NewRefEncryption(len(accessKey)) enc := NewRefEncryption(len(accessKey))
encryptedAccessKey, err := enc.Encrypt(accessKey, accessKeyEncryptionKey) encryptedAccessKey, err := enc.Encrypt(accessKey, accessKeyEncryptionKey)
if err != nil {
return nil, nil, nil, err
}
lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey)
}
for _, pass := range encryptPasswords {
sessionKey, err := sessionKeyPassword(pass, salt, DefaultKdfParams)
if err != nil {
return nil, nil, nil, err
}
hasher := sha3.NewKeccak256()
hasher.Write(append(sessionKey, 0))
lookupKey := hasher.Sum(nil)
hasher.Reset()
hasher.Write(append(sessionKey, 1))
accessKeyEncryptionKey := hasher.Sum(nil)
enc := NewRefEncryption(len(accessKey))
encryptedAccessKey, err := enc.Encrypt(accessKey, accessKeyEncryptionKey)
if err != nil {
return nil, nil, nil, err
}
lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey) lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey)
} }
@ -454,7 +518,10 @@ func DoACTNew(ctx *cli.Context, privateKey *ecdsa.PrivateKey, salt []byte, grant
return accessKey, ae, m, nil return accessKey, ae, m, nil
} }
func DoPasswordNew(ctx *cli.Context, password string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) { // DoPassword is a helper function to the CLI API that handles the entire business logic for
// creating a session key and an access entry given the cli context, password and salt.
// By default - DefaultKdfParams are used as the scrypt params
func DoPassword(ctx *cli.Context, password string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) {
ae, err = NewAccessEntryPassword(salt, DefaultKdfParams) ae, err = NewAccessEntryPassword(salt, DefaultKdfParams)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

44
swarm/testutil/file.go Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package testutil
import (
"io"
"io/ioutil"
"os"
"strings"
"testing"
)
// TempFileWithContent is a helper function that creates a temp file that contains the following string content then closes the file handle
// it returns the complete file path
func TempFileWithContent(t *testing.T, content string) string {
tempFile, err := ioutil.TempFile("", "swarm-temp-file")
if err != nil {
t.Fatal(err)
}
_, err = io.Copy(tempFile, strings.NewReader(content))
if err != nil {
os.RemoveAll(tempFile.Name())
t.Fatal(err)
}
if err = tempFile.Close(); err != nil {
t.Fatal(err)
}
return tempFile.Name()
}