aboutsummaryrefslogtreecommitdiffstats
path: root/swarm
diff options
context:
space:
mode:
Diffstat (limited to 'swarm')
-rw-r--r--swarm/api/act.go468
-rw-r--r--swarm/api/api.go129
-rw-r--r--swarm/api/api_test.go68
-rw-r--r--swarm/api/client/client.go48
-rw-r--r--swarm/api/client/client_test.go4
-rw-r--r--swarm/api/config.go2
-rw-r--r--swarm/api/encrypt.go76
-rw-r--r--swarm/api/filesystem.go4
-rw-r--r--swarm/api/filesystem_test.go4
-rw-r--r--swarm/api/http/middleware.go12
-rw-r--r--swarm/api/http/response.go2
-rw-r--r--swarm/api/http/server.go111
-rw-r--r--swarm/api/manifest.go69
-rw-r--r--swarm/api/manifest_test.go8
-rw-r--r--swarm/api/storage.go6
-rw-r--r--swarm/api/uri.go13
-rw-r--r--swarm/bmt/bmt.go5
-rw-r--r--swarm/bmt/bmt_test.go19
-rw-r--r--swarm/chunk/chunk.go5
-rw-r--r--swarm/fuse/swarmfs_test.go2
-rw-r--r--swarm/network/stream/delivery.go7
-rw-r--r--swarm/network_test.go2
-rw-r--r--swarm/sctx/sctx.go15
-rw-r--r--swarm/storage/chunker.go9
-rw-r--r--swarm/storage/hasherstore.go9
-rw-r--r--swarm/storage/ldbstore.go67
-rw-r--r--swarm/storage/ldbstore_test.go11
-rw-r--r--swarm/storage/localstore.go10
-rw-r--r--swarm/storage/localstore_test.go8
-rw-r--r--swarm/storage/mru/handler.go25
-rw-r--r--swarm/storage/mru/resource_test.go8
-rw-r--r--swarm/storage/mru/testutil.go5
-rw-r--r--swarm/storage/mru/update.go3
-rw-r--r--swarm/storage/pyramid.go5
-rw-r--r--swarm/storage/types.go13
-rw-r--r--swarm/swarm.go17
-rw-r--r--swarm/testutil/http.go2
37 files changed, 1021 insertions, 250 deletions
diff --git a/swarm/api/act.go b/swarm/api/act.go
new file mode 100644
index 000000000..b1a594783
--- /dev/null
+++ b/swarm/api/act.go
@@ -0,0 +1,468 @@
+package api
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/ecies"
+ "github.com/ethereum/go-ethereum/crypto/sha3"
+ "github.com/ethereum/go-ethereum/swarm/log"
+ "github.com/ethereum/go-ethereum/swarm/sctx"
+ "github.com/ethereum/go-ethereum/swarm/storage"
+ "golang.org/x/crypto/scrypt"
+ cli "gopkg.in/urfave/cli.v1"
+)
+
+var (
+ ErrDecrypt = errors.New("cant decrypt - forbidden")
+ ErrUnknownAccessType = errors.New("unknown access type (or not implemented)")
+ ErrDecryptDomainForbidden = errors.New("decryption request domain forbidden - can only decrypt on localhost")
+ AllowedDecryptDomains = []string{
+ "localhost",
+ "127.0.0.1",
+ }
+)
+
+const EMPTY_CREDENTIALS = ""
+
+type AccessEntry struct {
+ Type AccessType
+ Publisher string
+ Salt []byte
+ Act string
+ KdfParams *KdfParams
+}
+
+type DecryptFunc func(*ManifestEntry) error
+
+func (a *AccessEntry) MarshalJSON() (out []byte, err error) {
+
+ return json.Marshal(struct {
+ Type AccessType `json:"type,omitempty"`
+ Publisher string `json:"publisher,omitempty"`
+ Salt string `json:"salt,omitempty"`
+ Act string `json:"act,omitempty"`
+ KdfParams *KdfParams `json:"kdf_params,omitempty"`
+ }{
+ Type: a.Type,
+ Publisher: a.Publisher,
+ Salt: hex.EncodeToString(a.Salt),
+ Act: a.Act,
+ KdfParams: a.KdfParams,
+ })
+
+}
+
+func (a *AccessEntry) UnmarshalJSON(value []byte) error {
+ v := struct {
+ Type AccessType `json:"type,omitempty"`
+ Publisher string `json:"publisher,omitempty"`
+ Salt string `json:"salt,omitempty"`
+ Act string `json:"act,omitempty"`
+ KdfParams *KdfParams `json:"kdf_params,omitempty"`
+ }{}
+
+ err := json.Unmarshal(value, &v)
+ if err != nil {
+ return err
+ }
+ a.Act = v.Act
+ a.KdfParams = v.KdfParams
+ a.Publisher = v.Publisher
+ a.Salt, err = hex.DecodeString(v.Salt)
+ if err != nil {
+ return err
+ }
+ if len(a.Salt) != 32 {
+ return errors.New("salt should be 32 bytes long")
+ }
+ a.Type = v.Type
+ return nil
+}
+
+type KdfParams struct {
+ N int `json:"n"`
+ P int `json:"p"`
+ R int `json:"r"`
+}
+
+type AccessType string
+
+const AccessTypePass = AccessType("pass")
+const AccessTypePK = AccessType("pk")
+const AccessTypeACT = AccessType("act")
+
+func NewAccessEntryPassword(salt []byte, kdfParams *KdfParams) (*AccessEntry, error) {
+ if len(salt) != 32 {
+ return nil, fmt.Errorf("salt should be 32 bytes long")
+ }
+ return &AccessEntry{
+ Type: AccessTypePass,
+ Salt: salt,
+ KdfParams: kdfParams,
+ }, nil
+}
+
+func NewAccessEntryPK(publisher string, salt []byte) (*AccessEntry, error) {
+ if len(publisher) != 66 {
+ return nil, fmt.Errorf("publisher should be 66 characters long, got %d", len(publisher))
+ }
+ if len(salt) != 32 {
+ return nil, fmt.Errorf("salt should be 32 bytes long")
+ }
+ return &AccessEntry{
+ Type: AccessTypePK,
+ Publisher: publisher,
+ Salt: salt,
+ }, nil
+}
+
+func NewAccessEntryACT(publisher string, salt []byte, act string) (*AccessEntry, error) {
+ if len(salt) != 32 {
+ return nil, fmt.Errorf("salt should be 32 bytes long")
+ }
+ if len(publisher) != 66 {
+ return nil, fmt.Errorf("publisher should be 66 characters long")
+ }
+
+ return &AccessEntry{
+ Type: AccessTypeACT,
+ Publisher: publisher,
+ Salt: salt,
+ Act: act,
+ }, nil
+}
+
+func NOOPDecrypt(*ManifestEntry) error {
+ return nil
+}
+
+var DefaultKdfParams = NewKdfParams(262144, 1, 8)
+
+func NewKdfParams(n, p, r int) *KdfParams {
+
+ return &KdfParams{
+ N: n,
+ P: p,
+ R: r,
+ }
+}
+
+// NewSessionKeyPassword creates a session key based on a shared secret (password) and the given salt
+// and kdf parameters in the access entry
+func NewSessionKeyPassword(password string, accessEntry *AccessEntry) ([]byte, error) {
+ if accessEntry.Type != AccessTypePass {
+ return nil, errors.New("incorrect access entry type")
+ }
+ return scrypt.Key(
+ []byte(password),
+ accessEntry.Salt,
+ accessEntry.KdfParams.N,
+ accessEntry.KdfParams.R,
+ accessEntry.KdfParams.P,
+ 32,
+ )
+}
+
+// NewSessionKeyPK creates a new ACT Session Key using an ECDH shared secret for the given key pair and the given salt value
+func NewSessionKeyPK(private *ecdsa.PrivateKey, public *ecdsa.PublicKey, salt []byte) ([]byte, error) {
+ granteePubEcies := ecies.ImportECDSAPublic(public)
+ privateKey := ecies.ImportECDSA(private)
+
+ bytes, err := privateKey.GenerateShared(granteePubEcies, 16, 16)
+ if err != nil {
+ return nil, err
+ }
+ bytes = append(salt, bytes...)
+ sessionKey := crypto.Keccak256(bytes)
+ 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 {
+ return func(m *ManifestEntry) error {
+ if m.Access == nil {
+ return nil
+ }
+
+ allowed := false
+ requestDomain := sctx.GetHost(ctx)
+ for _, v := range AllowedDecryptDomains {
+ if strings.Contains(requestDomain, v) {
+ allowed = true
+ }
+ }
+
+ if !allowed {
+ return ErrDecryptDomainForbidden
+ }
+
+ switch m.Access.Type {
+ case "pass":
+ if credentials != "" {
+ key, err := NewSessionKeyPassword(credentials, m.Access)
+ if err != nil {
+ return err
+ }
+
+ ref, err := hex.DecodeString(m.Hash)
+ if err != nil {
+ return err
+ }
+
+ enc := NewRefEncryption(len(ref) - 8)
+ decodedRef, err := enc.Decrypt(ref, key)
+ if err != nil {
+ return ErrDecrypt
+ }
+
+ m.Hash = hex.EncodeToString(decodedRef)
+ m.Access = nil
+ return nil
+ }
+ return ErrDecrypt
+ case "pk":
+ publisherBytes, err := hex.DecodeString(m.Access.Publisher)
+ if err != nil {
+ return ErrDecrypt
+ }
+ publisher, err := crypto.DecompressPubkey(publisherBytes)
+ if err != nil {
+ return ErrDecrypt
+ }
+ key, err := a.NodeSessionKey(pk, publisher, m.Access.Salt)
+ if err != nil {
+ return ErrDecrypt
+ }
+ ref, err := hex.DecodeString(m.Hash)
+ if err != nil {
+ return err
+ }
+
+ enc := NewRefEncryption(len(ref) - 8)
+ decodedRef, err := enc.Decrypt(ref, key)
+ if err != nil {
+ return ErrDecrypt
+ }
+
+ m.Hash = hex.EncodeToString(decodedRef)
+ m.Access = nil
+ return nil
+ case "act":
+ publisherBytes, err := hex.DecodeString(m.Access.Publisher)
+ if err != nil {
+ return ErrDecrypt
+ }
+ publisher, err := crypto.DecompressPubkey(publisherBytes)
+ if err != nil {
+ return ErrDecrypt
+ }
+
+ sessionKey, err := a.NodeSessionKey(pk, publisher, m.Access.Salt)
+ if err != nil {
+ return ErrDecrypt
+ }
+
+ hasher := sha3.NewKeccak256()
+ 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 {
+ return err
+ }
+ enc := NewRefEncryption(len(v) - 8)
+ decodedRef, err := enc.Decrypt(v, accessKeyDecryptionKey)
+ if err != nil {
+ return ErrDecrypt
+ }
+
+ ref, err := hex.DecodeString(m.Hash)
+ if err != nil {
+ return err
+ }
+
+ enc = NewRefEncryption(len(ref) - 8)
+ decodedMainRef, err := enc.Decrypt(ref, decodedRef)
+ if err != nil {
+ return ErrDecrypt
+ }
+ m.Hash = hex.EncodeToString(decodedMainRef)
+ m.Access = nil
+ return nil
+ }
+ return ErrUnknownAccessType
+ }
+}
+
+func GenerateAccessControlManifest(ctx *cli.Context, ref string, accessKey []byte, ae *AccessEntry) (*Manifest, error) {
+ refBytes, err := hex.DecodeString(ref)
+ if err != nil {
+ return nil, err
+ }
+ // encrypt ref with accessKey
+ enc := NewRefEncryption(len(refBytes))
+ encrypted, err := enc.Encrypt(refBytes, accessKey)
+ if err != nil {
+ return nil, err
+ }
+
+ m := &Manifest{
+ Entries: []ManifestEntry{
+ {
+ Hash: hex.EncodeToString(encrypted),
+ ContentType: ManifestType,
+ ModTime: time.Now(),
+ Access: ae,
+ },
+ },
+ }
+
+ return m, nil
+}
+
+func DoPKNew(ctx *cli.Context, privateKey *ecdsa.PrivateKey, granteePublicKey string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) {
+ if granteePublicKey == "" {
+ return nil, nil, errors.New("need a grantee Public Key")
+ }
+ b, err := hex.DecodeString(granteePublicKey)
+ if err != nil {
+ log.Error("error decoding grantee public key", "err", err)
+ return nil, nil, err
+ }
+
+ granteePub, err := crypto.DecompressPubkey(b)
+ if err != nil {
+ log.Error("error decompressing grantee public key", "err", err)
+ return nil, nil, err
+ }
+
+ sessionKey, err = NewSessionKeyPK(privateKey, granteePub, salt)
+ if err != nil {
+ log.Error("error getting session key", "err", err)
+ return nil, nil, err
+ }
+
+ ae, err = NewAccessEntryPK(hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)), salt)
+ if err != nil {
+ log.Error("error generating access entry", "err", err)
+ return nil, nil, err
+ }
+
+ return sessionKey, ae, nil
+}
+
+func DoACTNew(ctx *cli.Context, privateKey *ecdsa.PrivateKey, salt []byte, grantees []string) (accessKey []byte, ae *AccessEntry, actManifest *Manifest, err error) {
+ if len(grantees) == 0 {
+ return nil, nil, nil, errors.New("did not get any grantee public keys")
+ }
+
+ publisherPub := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
+ grantees = append(grantees, publisherPub)
+
+ accessKey = make([]byte, 32)
+ if _, err := io.ReadFull(rand.Reader, salt); err != nil {
+ panic("reading from crypto/rand failed: " + err.Error())
+ }
+ if _, err := io.ReadFull(rand.Reader, accessKey); err != nil {
+ panic("reading from crypto/rand failed: " + err.Error())
+ }
+
+ lookupPathEncryptedAccessKeyMap := make(map[string]string)
+ i := 0
+ for _, v := range grantees {
+ i++
+ if v == "" {
+ return nil, nil, nil, errors.New("need a grantee Public Key")
+ }
+ b, err := hex.DecodeString(v)
+ if err != nil {
+ log.Error("error decoding grantee public key", "err", err)
+ return nil, nil, nil, err
+ }
+
+ granteePub, err := crypto.DecompressPubkey(b)
+ if err != nil {
+ log.Error("error decompressing grantee public key", "err", err)
+ return nil, nil, nil, err
+ }
+ sessionKey, err := NewSessionKeyPK(privateKey, granteePub, salt)
+
+ 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)
+
+ lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey)
+ }
+
+ m := &Manifest{
+ Entries: []ManifestEntry{},
+ }
+
+ for k, v := range lookupPathEncryptedAccessKeyMap {
+ m.Entries = append(m.Entries, ManifestEntry{
+ Path: k,
+ Hash: v,
+ ContentType: "text/plain",
+ })
+ }
+
+ ae, err = NewAccessEntryACT(hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)), salt, "")
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ return accessKey, ae, m, nil
+}
+
+func DoPasswordNew(ctx *cli.Context, password string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) {
+ ae, err = NewAccessEntryPassword(salt, DefaultKdfParams)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ sessionKey, err = NewSessionKeyPassword(password, ae)
+ if err != nil {
+ return nil, nil, err
+ }
+ return sessionKey, ae, nil
+}
diff --git a/swarm/api/api.go b/swarm/api/api.go
index 99d971b10..adf469cfa 100644
--- a/swarm/api/api.go
+++ b/swarm/api/api.go
@@ -19,6 +19,9 @@ package api
import (
"archive/tar"
"context"
+ "crypto/ecdsa"
+ "encoding/hex"
+ "errors"
"fmt"
"io"
"math/big"
@@ -44,6 +47,10 @@ import (
)
var (
+ ErrNotFound = errors.New("not found")
+)
+
+var (
apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil)
apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil)
apiPutCount = metrics.NewRegisteredCounter("api.put.count", nil)
@@ -227,14 +234,18 @@ type API struct {
resource *mru.Handler
fileStore *storage.FileStore
dns Resolver
+ Decryptor func(context.Context, string) DecryptFunc
}
// NewAPI the api constructor initialises a new API instance.
-func NewAPI(fileStore *storage.FileStore, dns Resolver, resourceHandler *mru.Handler) (self *API) {
+func NewAPI(fileStore *storage.FileStore, dns Resolver, resourceHandler *mru.Handler, pk *ecdsa.PrivateKey) (self *API) {
self = &API{
fileStore: fileStore,
dns: dns,
resource: resourceHandler,
+ Decryptor: func(ctx context.Context, credentials string) DecryptFunc {
+ return self.doDecrypt(ctx, credentials, pk)
+ },
}
return
}
@@ -260,8 +271,30 @@ func (a *API) Store(ctx context.Context, data io.Reader, size int64, toEncrypt b
// ErrResolve is returned when an URI cannot be resolved from ENS.
type ErrResolve error
+// Resolve a name into a content-addressed hash
+// where address could be an ENS name, or a content addressed hash
+func (a *API) Resolve(ctx context.Context, address string) (storage.Address, error) {
+ // if DNS is not configured, return an error
+ if a.dns == nil {
+ if hashMatcher.MatchString(address) {
+ return common.Hex2Bytes(address), nil
+ }
+ apiResolveFail.Inc(1)
+ return nil, fmt.Errorf("no DNS to resolve name: %q", address)
+ }
+ // try and resolve the address
+ resolved, err := a.dns.Resolve(address)
+ if err != nil {
+ if hashMatcher.MatchString(address) {
+ return common.Hex2Bytes(address), nil
+ }
+ return nil, err
+ }
+ return resolved[:], nil
+}
+
// Resolve resolves a URI to an Address using the MultiResolver.
-func (a *API) Resolve(ctx context.Context, uri *URI) (storage.Address, error) {
+func (a *API) ResolveURI(ctx context.Context, uri *URI, credentials string) (storage.Address, error) {
apiResolveCount.Inc(1)
log.Trace("resolving", "uri", uri.Addr)
@@ -280,28 +313,44 @@ func (a *API) Resolve(ctx context.Context, uri *URI) (storage.Address, error) {
return key, nil
}
- // if DNS is not configured, check if the address is a hash
- if a.dns == nil {
- key := uri.Address()
- if key == nil {
- apiResolveFail.Inc(1)
- return nil, fmt.Errorf("no DNS to resolve name: %q", uri.Addr)
- }
- return key, nil
+ addr, err := a.Resolve(ctx, uri.Addr)
+ if err != nil {
+ return nil, err
}
- // try and resolve the address
- resolved, err := a.dns.Resolve(uri.Addr)
- if err == nil {
- return resolved[:], nil
+ if uri.Path == "" {
+ return addr, nil
}
-
- key := uri.Address()
- if key == nil {
- apiResolveFail.Inc(1)
+ walker, err := a.NewManifestWalker(ctx, addr, a.Decryptor(ctx, credentials), nil)
+ if err != nil {
return nil, err
}
- return key, nil
+ var entry *ManifestEntry
+ walker.Walk(func(e *ManifestEntry) error {
+ // if the entry matches the path, set entry and stop
+ // the walk
+ if e.Path == uri.Path {
+ entry = e
+ // return an error to cancel the walk
+ return errors.New("found")
+ }
+ // ignore non-manifest files
+ if e.ContentType != ManifestType {
+ return nil
+ }
+ // if the manifest's path is a prefix of the
+ // requested path, recurse into it by returning
+ // nil and continuing the walk
+ if strings.HasPrefix(uri.Path, e.Path) {
+ return nil
+ }
+ return ErrSkipManifest
+ })
+ if entry == nil {
+ return nil, errors.New("not found")
+ }
+ addr = storage.Address(common.Hex2Bytes(entry.Hash))
+ return addr, nil
}
// Put provides singleton manifest creation on top of FileStore store
@@ -332,10 +381,10 @@ func (a *API) Put(ctx context.Context, content string, contentType string, toEnc
// Get uses iterative manifest retrieval and prefix matching
// to resolve basePath to content using FileStore retrieve
// it returns a section reader, mimeType, status, the key of the actual content and an error
-func (a *API) Get(ctx context.Context, manifestAddr storage.Address, path string) (reader storage.LazySectionReader, mimeType string, status int, contentAddr storage.Address, err error) {
+func (a *API) Get(ctx context.Context, decrypt DecryptFunc, manifestAddr storage.Address, path string) (reader storage.LazySectionReader, mimeType string, status int, contentAddr storage.Address, err error) {
log.Debug("api.get", "key", manifestAddr, "path", path)
apiGetCount.Inc(1)
- trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil)
+ trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, decrypt)
if err != nil {
apiGetNotFound.Inc(1)
status = http.StatusNotFound
@@ -347,6 +396,16 @@ func (a *API) Get(ctx context.Context, manifestAddr storage.Address, path string
if entry != nil {
log.Debug("trie got entry", "key", manifestAddr, "path", path, "entry.Hash", entry.Hash)
+
+ if entry.ContentType == ManifestType {
+ log.Debug("entry is manifest", "key", manifestAddr, "new key", entry.Hash)
+ adr, err := hex.DecodeString(entry.Hash)
+ if err != nil {
+ return nil, "", 0, nil, err
+ }
+ return a.Get(ctx, decrypt, adr, entry.Path)
+ }
+
// we need to do some extra work if this is a mutable resource manifest
if entry.ContentType == ResourceContentType {
@@ -398,7 +457,7 @@ func (a *API) Get(ctx context.Context, manifestAddr storage.Address, path string
log.Trace("resource is multihash", "key", manifestAddr)
// get the manifest the multihash digest points to
- trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil)
+ trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, decrypt)
if err != nil {
apiGetNotFound.Inc(1)
status = http.StatusNotFound
@@ -451,7 +510,7 @@ func (a *API) Delete(ctx context.Context, addr string, path string) (storage.Add
apiDeleteFail.Inc(1)
return nil, err
}
- key, err := a.Resolve(ctx, uri)
+ key, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
if err != nil {
return nil, err
@@ -470,13 +529,13 @@ func (a *API) Delete(ctx context.Context, addr string, path string) (storage.Add
// GetDirectoryTar fetches a requested directory as a tarstream
// it returns an io.Reader and an error. Do not forget to Close() the returned ReadCloser
-func (a *API) GetDirectoryTar(ctx context.Context, uri *URI) (io.ReadCloser, error) {
+func (a *API) GetDirectoryTar(ctx context.Context, decrypt DecryptFunc, uri *URI) (io.ReadCloser, error) {
apiGetTarCount.Inc(1)
- addr, err := a.Resolve(ctx, uri)
+ addr, err := a.Resolve(ctx, uri.Addr)
if err != nil {
return nil, err
}
- walker, err := a.NewManifestWalker(ctx, addr, nil)
+ walker, err := a.NewManifestWalker(ctx, addr, decrypt, nil)
if err != nil {
apiGetTarFail.Inc(1)
return nil, err
@@ -542,9 +601,9 @@ func (a *API) GetDirectoryTar(ctx context.Context, uri *URI) (io.ReadCloser, err
// GetManifestList lists the manifest entries for the specified address and prefix
// and returns it as a ManifestList
-func (a *API) GetManifestList(ctx context.Context, addr storage.Address, prefix string) (list ManifestList, err error) {
+func (a *API) GetManifestList(ctx context.Context, decryptor DecryptFunc, addr storage.Address, prefix string) (list ManifestList, err error) {
apiManifestListCount.Inc(1)
- walker, err := a.NewManifestWalker(ctx, addr, nil)
+ walker, err := a.NewManifestWalker(ctx, addr, decryptor, nil)
if err != nil {
apiManifestListFail.Inc(1)
return ManifestList{}, err
@@ -631,7 +690,7 @@ func (a *API) UpdateManifest(ctx context.Context, addr storage.Address, update f
func (a *API) Modify(ctx context.Context, addr storage.Address, path, contentHash, contentType string) (storage.Address, error) {
apiModifyCount.Inc(1)
quitC := make(chan bool)
- trie, err := loadManifest(ctx, a.fileStore, addr, quitC)
+ trie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt)
if err != nil {
apiModifyFail.Inc(1)
return nil, err
@@ -663,7 +722,7 @@ func (a *API) AddFile(ctx context.Context, mhash, path, fname string, content []
apiAddFileFail.Inc(1)
return nil, "", err
}
- mkey, err := a.Resolve(ctx, uri)
+ mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
if err != nil {
apiAddFileFail.Inc(1)
return nil, "", err
@@ -770,7 +829,7 @@ func (a *API) RemoveFile(ctx context.Context, mhash string, path string, fname s
apiRmFileFail.Inc(1)
return "", err
}
- mkey, err := a.Resolve(ctx, uri)
+ mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
if err != nil {
apiRmFileFail.Inc(1)
return "", err
@@ -837,7 +896,7 @@ func (a *API) AppendFile(ctx context.Context, mhash, path, fname string, existin
apiAppendFileFail.Inc(1)
return nil, "", err
}
- mkey, err := a.Resolve(ctx, uri)
+ mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
if err != nil {
apiAppendFileFail.Inc(1)
return nil, "", err
@@ -891,13 +950,13 @@ func (a *API) BuildDirectoryTree(ctx context.Context, mhash string, nameresolver
if err != nil {
return nil, nil, err
}
- addr, err = a.Resolve(ctx, uri)
+ addr, err = a.Resolve(ctx, uri.Addr)
if err != nil {
return nil, nil, err
}
quitC := make(chan bool)
- rootTrie, err := loadManifest(ctx, a.fileStore, addr, quitC)
+ rootTrie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt)
if err != nil {
return nil, nil, fmt.Errorf("can't load manifest %v: %v", addr.String(), err)
}
@@ -955,7 +1014,7 @@ func (a *API) ResourceHashSize() int {
// ResolveResourceManifest retrieves the Mutable Resource manifest for the given address, and returns the address of the metadata chunk.
func (a *API) ResolveResourceManifest(ctx context.Context, addr storage.Address) (storage.Address, error) {
- trie, err := loadManifest(ctx, a.fileStore, addr, nil)
+ trie, err := loadManifest(ctx, a.fileStore, addr, nil, NOOPDecrypt)
if err != nil {
return nil, fmt.Errorf("cannot load resource manifest: %v", err)
}
diff --git a/swarm/api/api_test.go b/swarm/api/api_test.go
index 78fab9508..a65bf07e2 100644
--- a/swarm/api/api_test.go
+++ b/swarm/api/api_test.go
@@ -19,6 +19,7 @@ package api
import (
"context"
"errors"
+ "flag"
"fmt"
"io"
"io/ioutil"
@@ -28,10 +29,17 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/swarm/log"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/swarm/sctx"
"github.com/ethereum/go-ethereum/swarm/storage"
)
+func init() {
+ loglevel := flag.Int("loglevel", 2, "loglevel")
+ flag.Parse()
+ log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))))
+}
+
func testAPI(t *testing.T, f func(*API, bool)) {
datadir, err := ioutil.TempDir("", "bzz-test")
if err != nil {
@@ -42,7 +50,7 @@ func testAPI(t *testing.T, f func(*API, bool)) {
if err != nil {
return
}
- api := NewAPI(fileStore, nil, nil)
+ api := NewAPI(fileStore, nil, nil, nil)
f(api, false)
f(api, true)
}
@@ -85,7 +93,7 @@ func expResponse(content string, mimeType string, status int) *Response {
func testGet(t *testing.T, api *API, bzzhash, path string) *testResponse {
addr := storage.Address(common.Hex2Bytes(bzzhash))
- reader, mimeType, status, _, err := api.Get(context.TODO(), addr, path)
+ reader, mimeType, status, _, err := api.Get(context.TODO(), NOOPDecrypt, addr, path)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -229,7 +237,7 @@ func TestAPIResolve(t *testing.T) {
if x.immutable {
uri.Scheme = "bzz-immutable"
}
- res, err := api.Resolve(context.TODO(), uri)
+ res, err := api.ResolveURI(context.TODO(), uri, "")
if err == nil {
if x.expectErr != nil {
t.Fatalf("expected error %q, got result %q", x.expectErr, res)
@@ -373,3 +381,55 @@ func TestMultiResolver(t *testing.T) {
})
}
}
+
+func TestDecryptOriginForbidden(t *testing.T) {
+ ctx := context.TODO()
+ ctx = sctx.SetHost(ctx, "swarm-gateways.net")
+
+ me := &ManifestEntry{
+ Access: &AccessEntry{Type: AccessTypePass},
+ }
+
+ api := NewAPI(nil, nil, nil, nil)
+
+ f := api.Decryptor(ctx, "")
+ err := f(me)
+ if err != ErrDecryptDomainForbidden {
+ t.Fatalf("should fail with ErrDecryptDomainForbidden, got %v", err)
+ }
+}
+
+func TestDecryptOrigin(t *testing.T) {
+ for _, v := range []struct {
+ host string
+ expectError error
+ }{
+ {
+ host: "localhost",
+ expectError: ErrDecrypt,
+ },
+ {
+ host: "127.0.0.1",
+ expectError: ErrDecrypt,
+ },
+ {
+ host: "swarm-gateways.net",
+ expectError: ErrDecryptDomainForbidden,
+ },
+ } {
+ ctx := context.TODO()
+ ctx = sctx.SetHost(ctx, v.host)
+
+ me := &ManifestEntry{
+ Access: &AccessEntry{Type: AccessTypePass},
+ }
+
+ api := NewAPI(nil, nil, nil, nil)
+
+ f := api.Decryptor(ctx, "")
+ err := f(me)
+ if err != v.expectError {
+ t.Fatalf("should fail with %v, got %v", v.expectError, err)
+ }
+ }
+}
diff --git a/swarm/api/client/client.go b/swarm/api/client/client.go
index 8a9efe360..3d06e9e1c 100644
--- a/swarm/api/client/client.go
+++ b/swarm/api/client/client.go
@@ -43,6 +43,10 @@ var (
DefaultClient = NewClient(DefaultGateway)
)
+var (
+ ErrUnauthorized = errors.New("unauthorized")
+)
+
func NewClient(gateway string) *Client {
return &Client{
Gateway: gateway,
@@ -188,7 +192,7 @@ func (c *Client) UploadDirectory(dir, defaultPath, manifest string, toEncrypt bo
// DownloadDirectory downloads the files contained in a swarm manifest under
// the given path into a local directory (existing files will be overwritten)
-func (c *Client) DownloadDirectory(hash, path, destDir string) error {
+func (c *Client) DownloadDirectory(hash, path, destDir, credentials string) error {
stat, err := os.Stat(destDir)
if err != nil {
return err
@@ -201,13 +205,20 @@ func (c *Client) DownloadDirectory(hash, path, destDir string) error {
if err != nil {
return err
}
+ if credentials != "" {
+ req.SetBasicAuth("", credentials)
+ }
req.Header.Set("Accept", "application/x-tar")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
- if res.StatusCode != http.StatusOK {
+ switch res.StatusCode {
+ case http.StatusOK:
+ case http.StatusUnauthorized:
+ return ErrUnauthorized
+ default:
return fmt.Errorf("unexpected HTTP status: %s", res.Status)
}
tr := tar.NewReader(res.Body)
@@ -248,7 +259,7 @@ func (c *Client) DownloadDirectory(hash, path, destDir string) error {
// DownloadFile downloads a single file into the destination directory
// if the manifest entry does not specify a file name - it will fallback
// to the hash of the file as a filename
-func (c *Client) DownloadFile(hash, path, dest string) error {
+func (c *Client) DownloadFile(hash, path, dest, credentials string) error {
hasDestinationFilename := false
if stat, err := os.Stat(dest); err == nil {
hasDestinationFilename = !stat.IsDir()
@@ -261,9 +272,9 @@ func (c *Client) DownloadFile(hash, path, dest string) error {
}
}
- manifestList, err := c.List(hash, path)
+ manifestList, err := c.List(hash, path, credentials)
if err != nil {
- return fmt.Errorf("could not list manifest: %v", err)
+ return err
}
switch len(manifestList.Entries) {
@@ -280,13 +291,19 @@ func (c *Client) DownloadFile(hash, path, dest string) error {
if err != nil {
return err
}
+ if credentials != "" {
+ req.SetBasicAuth("", credentials)
+ }
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
-
- if res.StatusCode != http.StatusOK {
+ switch res.StatusCode {
+ case http.StatusOK:
+ case http.StatusUnauthorized:
+ return ErrUnauthorized
+ default:
return fmt.Errorf("unexpected HTTP status: expected 200 OK, got %d", res.StatusCode)
}
filename := ""
@@ -367,13 +384,24 @@ func (c *Client) DownloadManifest(hash string) (*api.Manifest, bool, error) {
// - a prefix of "dir1/" would return [dir1/dir2/, dir1/file3.txt]
//
// where entries ending with "/" are common prefixes.
-func (c *Client) List(hash, prefix string) (*api.ManifestList, error) {
- res, err := http.DefaultClient.Get(c.Gateway + "/bzz-list:/" + hash + "/" + prefix)
+func (c *Client) List(hash, prefix, credentials string) (*api.ManifestList, error) {
+ req, err := http.NewRequest(http.MethodGet, c.Gateway+"/bzz-list:/"+hash+"/"+prefix, nil)
+ if err != nil {
+ return nil, err
+ }
+ if credentials != "" {
+ req.SetBasicAuth("", credentials)
+ }
+ res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
- if res.StatusCode != http.StatusOK {
+ switch res.StatusCode {
+ case http.StatusOK:
+ case http.StatusUnauthorized:
+ return nil, ErrUnauthorized
+ default:
return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status)
}
var list api.ManifestList
diff --git a/swarm/api/client/client_test.go b/swarm/api/client/client_test.go
index ae82a91d7..2212f5c4c 100644
--- a/swarm/api/client/client_test.go
+++ b/swarm/api/client/client_test.go
@@ -228,7 +228,7 @@ func TestClientUploadDownloadDirectory(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
- if err := client.DownloadDirectory(hash, "", tmp); err != nil {
+ if err := client.DownloadDirectory(hash, "", tmp, ""); err != nil {
t.Fatal(err)
}
for _, file := range testDirFiles {
@@ -265,7 +265,7 @@ func testClientFileList(toEncrypt bool, t *testing.T) {
}
ls := func(prefix string) []string {
- list, err := client.List(hash, prefix)
+ list, err := client.List(hash, prefix, "")
if err != nil {
t.Fatal(err)
}
diff --git a/swarm/api/config.go b/swarm/api/config.go
index bdfffdd05..3044dc2e5 100644
--- a/swarm/api/config.go
+++ b/swarm/api/config.go
@@ -68,7 +68,6 @@ type Config struct {
SwapAPI string
Cors string
BzzAccount string
- BootNodes string
privateKey *ecdsa.PrivateKey
}
@@ -93,7 +92,6 @@ func NewConfig() (c *Config) {
DeliverySkipCheck: false,
SyncUpdateDelay: 15 * time.Second,
SwapAPI: "",
- BootNodes: "",
}
return
diff --git a/swarm/api/encrypt.go b/swarm/api/encrypt.go
new file mode 100644
index 000000000..9a2e36914
--- /dev/null
+++ b/swarm/api/encrypt.go
@@ -0,0 +1,76 @@
+// Copyright 2016 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 api
+
+import (
+ "encoding/binary"
+ "errors"
+
+ "github.com/ethereum/go-ethereum/crypto/sha3"
+ "github.com/ethereum/go-ethereum/swarm/storage/encryption"
+)
+
+type RefEncryption struct {
+ spanEncryption encryption.Encryption
+ dataEncryption encryption.Encryption
+ span []byte
+}
+
+func NewRefEncryption(refSize int) *RefEncryption {
+ span := make([]byte, 8)
+ binary.LittleEndian.PutUint64(span, uint64(refSize))
+ return &RefEncryption{
+ spanEncryption: encryption.New(0, uint32(refSize/32), sha3.NewKeccak256),
+ dataEncryption: encryption.New(refSize, 0, sha3.NewKeccak256),
+ span: span,
+ }
+}
+
+func (re *RefEncryption) Encrypt(ref []byte, key []byte) ([]byte, error) {
+ encryptedSpan, err := re.spanEncryption.Encrypt(re.span, key)
+ if err != nil {
+ return nil, err
+ }
+ encryptedData, err := re.dataEncryption.Encrypt(ref, key)
+ if err != nil {
+ return nil, err
+ }
+ encryptedRef := make([]byte, len(ref)+8)
+ copy(encryptedRef[:8], encryptedSpan)
+ copy(encryptedRef[8:], encryptedData)
+
+ return encryptedRef, nil
+}
+
+func (re *RefEncryption) Decrypt(ref []byte, key []byte) ([]byte, error) {
+ decryptedSpan, err := re.spanEncryption.Decrypt(ref[:8], key)
+ if err != nil {
+ return nil, err
+ }
+
+ size := binary.LittleEndian.Uint64(decryptedSpan)
+ if size != uint64(len(ref)-8) {
+ return nil, errors.New("invalid span in encrypted reference")
+ }
+
+ decryptedRef, err := re.dataEncryption.Decrypt(ref[8:], key)
+ if err != nil {
+ return nil, err
+ }
+
+ return decryptedRef, nil
+}
diff --git a/swarm/api/filesystem.go b/swarm/api/filesystem.go
index aacd26699..8251ebc4d 100644
--- a/swarm/api/filesystem.go
+++ b/swarm/api/filesystem.go
@@ -191,7 +191,7 @@ func (fs *FileSystem) Download(bzzpath, localpath string) error {
if err != nil {
return err
}
- addr, err := fs.api.Resolve(context.TODO(), uri)
+ addr, err := fs.api.Resolve(context.TODO(), uri.Addr)
if err != nil {
return err
}
@@ -202,7 +202,7 @@ func (fs *FileSystem) Download(bzzpath, localpath string) error {
}
quitC := make(chan bool)
- trie, err := loadManifest(context.TODO(), fs.api.fileStore, addr, quitC)
+ trie, err := loadManifest(context.TODO(), fs.api.fileStore, addr, quitC, NOOPDecrypt)
if err != nil {
log.Warn(fmt.Sprintf("fs.Download: loadManifestTrie error: %v", err))
return err
diff --git a/swarm/api/filesystem_test.go b/swarm/api/filesystem_test.go
index 84a2989d6..fe7527b1f 100644
--- a/swarm/api/filesystem_test.go
+++ b/swarm/api/filesystem_test.go
@@ -64,7 +64,7 @@ func TestApiDirUpload0(t *testing.T) {
checkResponse(t, resp, exp)
addr := storage.Address(common.Hex2Bytes(bzzhash))
- _, _, _, _, err = api.Get(context.TODO(), addr, "")
+ _, _, _, _, err = api.Get(context.TODO(), NOOPDecrypt, addr, "")
if err == nil {
t.Fatalf("expected error: %v", err)
}
@@ -143,7 +143,7 @@ func TestApiDirUploadModify(t *testing.T) {
exp = expResponse(content, "text/css", 0)
checkResponse(t, resp, exp)
- _, _, _, _, err = api.Get(context.TODO(), addr, "")
+ _, _, _, _, err = api.Get(context.TODO(), nil, addr, "")
if err == nil {
t.Errorf("expected error: %v", err)
}
diff --git a/swarm/api/http/middleware.go b/swarm/api/http/middleware.go
index c0d8d1a40..3b2dcc7d5 100644
--- a/swarm/api/http/middleware.go
+++ b/swarm/api/http/middleware.go
@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/swarm/api"
"github.com/ethereum/go-ethereum/swarm/log"
+ "github.com/ethereum/go-ethereum/swarm/sctx"
"github.com/ethereum/go-ethereum/swarm/spancontext"
"github.com/pborman/uuid"
)
@@ -35,6 +36,15 @@ func SetRequestID(h http.Handler) http.Handler {
})
}
+func SetRequestHost(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ r = r.WithContext(sctx.SetHost(r.Context(), r.Host))
+ log.Info("setting request host", "ruid", GetRUID(r.Context()), "host", sctx.GetHost(r.Context()))
+
+ h.ServeHTTP(w, r)
+ })
+}
+
func ParseURI(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
@@ -87,7 +97,7 @@ func RecoverPanic(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
- log.Error("panic recovery!", "stack trace", debug.Stack(), "url", r.URL.String(), "headers", r.Header)
+ log.Error("panic recovery!", "stack trace", string(debug.Stack()), "url", r.URL.String(), "headers", r.Header)
}
}()
h.ServeHTTP(w, r)
diff --git a/swarm/api/http/response.go b/swarm/api/http/response.go
index f050e706a..c9fb9d285 100644
--- a/swarm/api/http/response.go
+++ b/swarm/api/http/response.go
@@ -79,7 +79,7 @@ func RespondTemplate(w http.ResponseWriter, r *http.Request, templateName, msg s
}
func RespondError(w http.ResponseWriter, r *http.Request, msg string, code int) {
- log.Debug("RespondError", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context()))
+ log.Debug("RespondError", "ruid", GetRUID(r.Context()), "uri", GetURI(r.Context()), "code", code)
RespondTemplate(w, r, "error", msg, code)
}
diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go
index 5a5c42adc..b5ea0c23d 100644
--- a/swarm/api/http/server.go
+++ b/swarm/api/http/server.go
@@ -23,7 +23,6 @@ import (
"bufio"
"bytes"
"encoding/json"
- "errors"
"fmt"
"io"
"io/ioutil"
@@ -97,6 +96,7 @@ func NewServer(api *api.API, corsString string) *Server {
defaultMiddlewares := []Adapter{
RecoverPanic,
SetRequestID,
+ SetRequestHost,
InitLoggingResponseWriter,
ParseURI,
InstrumentOpenTracing,
@@ -169,6 +169,7 @@ func NewServer(api *api.API, corsString string) *Server {
}
func (s *Server) ListenAndServe(addr string) error {
+ s.listenAddr = addr
return http.ListenAndServe(addr, s)
}
@@ -178,16 +179,24 @@ func (s *Server) ListenAndServe(addr string) error {
// https://github.com/atom/electron/blob/master/docs/api/protocol.md
type Server struct {
http.Handler
- api *api.API
+ api *api.API
+ listenAddr string
}
func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) {
- log.Debug("handleBzzGet", "ruid", GetRUID(r.Context()))
+ log.Debug("handleBzzGet", "ruid", GetRUID(r.Context()), "uri", r.RequestURI)
if r.Header.Get("Accept") == "application/x-tar" {
uri := GetURI(r.Context())
- reader, err := s.api.GetDirectoryTar(r.Context(), uri)
+ _, credentials, _ := r.BasicAuth()
+ reader, err := s.api.GetDirectoryTar(r.Context(), s.api.Decryptor(r.Context(), credentials), uri)
if err != nil {
+ if isDecryptError(err) {
+ w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", uri.Address().String()))
+ RespondError(w, r, err.Error(), http.StatusUnauthorized)
+ return
+ }
RespondError(w, r, fmt.Sprintf("Had an error building the tarball: %v", err), http.StatusInternalServerError)
+ return
}
defer reader.Close()
@@ -287,7 +296,7 @@ func (s *Server) HandlePostFiles(w http.ResponseWriter, r *http.Request) {
var addr storage.Address
if uri.Addr != "" && uri.Addr != "encrypt" {
- addr, err = s.api.Resolve(r.Context(), uri)
+ addr, err = s.api.Resolve(r.Context(), uri.Addr)
if err != nil {
postFilesFail.Inc(1)
RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusInternalServerError)
@@ -563,7 +572,7 @@ func (s *Server) HandleGetResource(w http.ResponseWriter, r *http.Request) {
// resolve the content key.
manifestAddr := uri.Address()
if manifestAddr == nil {
- manifestAddr, err = s.api.Resolve(r.Context(), uri)
+ manifestAddr, err = s.api.Resolve(r.Context(), uri.Addr)
if err != nil {
getFail.Inc(1)
RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
@@ -682,62 +691,21 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) {
uri := GetURI(r.Context())
log.Debug("handle.get", "ruid", ruid, "uri", uri)
getCount.Inc(1)
+ _, pass, _ := r.BasicAuth()
- var err error
- addr := uri.Address()
- if addr == nil {
- addr, err = s.api.Resolve(r.Context(), uri)
- if err != nil {
- getFail.Inc(1)
- RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
- return
- }
- } else {
- w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // url was of type bzz://<hex key>/path, so we are sure it is immutable.
+ addr, err := s.api.ResolveURI(r.Context(), uri, pass)
+ if err != nil {
+ getFail.Inc(1)
+ RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
+ return
}
+ w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // url was of type bzz://<hex key>/path, so we are sure it is immutable.
log.Debug("handle.get: resolved", "ruid", ruid, "key", addr)
// if path is set, interpret <key> as a manifest and return the
// raw entry at the given path
- if uri.Path != "" {
- walker, err := s.api.NewManifestWalker(r.Context(), addr, nil)
- if err != nil {
- getFail.Inc(1)
- RespondError(w, r, fmt.Sprintf("%s is not a manifest", addr), http.StatusBadRequest)
- return
- }
- var entry *api.ManifestEntry
- walker.Walk(func(e *api.ManifestEntry) error {
- // if the entry matches the path, set entry and stop
- // the walk
- if e.Path == uri.Path {
- entry = e
- // return an error to cancel the walk
- return errors.New("found")
- }
-
- // ignore non-manifest files
- if e.ContentType != api.ManifestType {
- return nil
- }
-
- // if the manifest's path is a prefix of the
- // requested path, recurse into it by returning
- // nil and continuing the walk
- if strings.HasPrefix(uri.Path, e.Path) {
- return nil
- }
- return api.ErrSkipManifest
- })
- if entry == nil {
- getFail.Inc(1)
- RespondError(w, r, fmt.Sprintf("manifest entry could not be loaded"), http.StatusNotFound)
- return
- }
- addr = storage.Address(common.Hex2Bytes(entry.Hash))
- }
etag := common.Bytes2Hex(addr)
noneMatchEtag := r.Header.Get("If-None-Match")
w.Header().Set("ETag", fmt.Sprintf("%q", etag)) // set etag to manifest key or raw entry key.
@@ -781,6 +749,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) {
func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) {
ruid := GetRUID(r.Context())
uri := GetURI(r.Context())
+ _, credentials, _ := r.BasicAuth()
log.Debug("handle.get.list", "ruid", ruid, "uri", uri)
getListCount.Inc(1)
@@ -790,7 +759,7 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) {
return
}
- addr, err := s.api.Resolve(r.Context(), uri)
+ addr, err := s.api.Resolve(r.Context(), uri.Addr)
if err != nil {
getListFail.Inc(1)
RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
@@ -798,9 +767,14 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) {
}
log.Debug("handle.get.list: resolved", "ruid", ruid, "key", addr)
- list, err := s.api.GetManifestList(r.Context(), addr, uri.Path)
+ list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), addr, uri.Path)
if err != nil {
getListFail.Inc(1)
+ if isDecryptError(err) {
+ w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", addr.String()))
+ RespondError(w, r, err.Error(), http.StatusUnauthorized)
+ return
+ }
RespondError(w, r, err.Error(), http.StatusInternalServerError)
return
}
@@ -833,7 +807,8 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) {
func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
ruid := GetRUID(r.Context())
uri := GetURI(r.Context())
- log.Debug("handle.get.file", "ruid", ruid)
+ _, credentials, _ := r.BasicAuth()
+ log.Debug("handle.get.file", "ruid", ruid, "uri", r.RequestURI)
getFileCount.Inc(1)
// ensure the root path has a trailing slash so that relative URLs work
@@ -845,7 +820,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
manifestAddr := uri.Address()
if manifestAddr == nil {
- manifestAddr, err = s.api.Resolve(r.Context(), uri)
+ manifestAddr, err = s.api.ResolveURI(r.Context(), uri, credentials)
if err != nil {
getFileFail.Inc(1)
RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
@@ -856,7 +831,8 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
}
log.Debug("handle.get.file: resolved", "ruid", ruid, "key", manifestAddr)
- reader, contentType, status, contentKey, err := s.api.Get(r.Context(), manifestAddr, uri.Path)
+
+ reader, contentType, status, contentKey, err := s.api.Get(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
etag := common.Bytes2Hex(contentKey)
noneMatchEtag := r.Header.Get("If-None-Match")
@@ -869,6 +845,12 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
}
if err != nil {
+ if isDecryptError(err) {
+ w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
+ RespondError(w, r, err.Error(), http.StatusUnauthorized)
+ return
+ }
+
switch status {
case http.StatusNotFound:
getFileNotFound.Inc(1)
@@ -883,9 +865,14 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
//the request results in ambiguous files
//e.g. /read with readme.md and readinglist.txt available in manifest
if status == http.StatusMultipleChoices {
- list, err := s.api.GetManifestList(r.Context(), manifestAddr, uri.Path)
+ list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
if err != nil {
getFileFail.Inc(1)
+ if isDecryptError(err) {
+ w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
+ RespondError(w, r, err.Error(), http.StatusUnauthorized)
+ return
+ }
RespondError(w, r, err.Error(), http.StatusInternalServerError)
return
}
@@ -951,3 +938,7 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
+
+func isDecryptError(err error) bool {
+ return strings.Contains(err.Error(), api.ErrDecrypt.Error())
+}
diff --git a/swarm/api/manifest.go b/swarm/api/manifest.go
index 2a163dd39..a1329a800 100644
--- a/swarm/api/manifest.go
+++ b/swarm/api/manifest.go
@@ -46,13 +46,14 @@ type Manifest struct {
// ManifestEntry represents an entry in a swarm manifest
type ManifestEntry struct {
- Hash string `json:"hash,omitempty"`
- Path string `json:"path,omitempty"`
- ContentType string `json:"contentType,omitempty"`
- Mode int64 `json:"mode,omitempty"`
- Size int64 `json:"size,omitempty"`
- ModTime time.Time `json:"mod_time,omitempty"`
- Status int `json:"status,omitempty"`
+ Hash string `json:"hash,omitempty"`
+ Path string `json:"path,omitempty"`
+ ContentType string `json:"contentType,omitempty"`
+ Mode int64 `json:"mode,omitempty"`
+ Size int64 `json:"size,omitempty"`
+ ModTime time.Time `json:"mod_time,omitempty"`
+ Status int `json:"status,omitempty"`
+ Access *AccessEntry `json:"access,omitempty"`
}
// ManifestList represents the result of listing files in a manifest
@@ -98,7 +99,7 @@ type ManifestWriter struct {
}
func (a *API) NewManifestWriter(ctx context.Context, addr storage.Address, quitC chan bool) (*ManifestWriter, error) {
- trie, err := loadManifest(ctx, a.fileStore, addr, quitC)
+ trie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt)
if err != nil {
return nil, fmt.Errorf("error loading manifest %s: %s", addr, err)
}
@@ -141,8 +142,8 @@ type ManifestWalker struct {
quitC chan bool
}
-func (a *API) NewManifestWalker(ctx context.Context, addr storage.Address, quitC chan bool) (*ManifestWalker, error) {
- trie, err := loadManifest(ctx, a.fileStore, addr, quitC)
+func (a *API) NewManifestWalker(ctx context.Context, addr storage.Address, decrypt DecryptFunc, quitC chan bool) (*ManifestWalker, error) {
+ trie, err := loadManifest(ctx, a.fileStore, addr, quitC, decrypt)
if err != nil {
return nil, fmt.Errorf("error loading manifest %s: %s", addr, err)
}
@@ -194,6 +195,7 @@ type manifestTrie struct {
entries [257]*manifestTrieEntry // indexed by first character of basePath, entries[256] is the empty basePath entry
ref storage.Address // if ref != nil, it is stored
encrypted bool
+ decrypt DecryptFunc
}
func newManifestTrieEntry(entry *ManifestEntry, subtrie *manifestTrie) *manifestTrieEntry {
@@ -209,15 +211,15 @@ type manifestTrieEntry struct {
subtrie *manifestTrie
}
-func loadManifest(ctx context.Context, fileStore *storage.FileStore, hash storage.Address, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand
+func loadManifest(ctx context.Context, fileStore *storage.FileStore, hash storage.Address, quitC chan bool, decrypt DecryptFunc) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand
log.Trace("manifest lookup", "key", hash)
// retrieve manifest via FileStore
manifestReader, isEncrypted := fileStore.Retrieve(ctx, hash)
log.Trace("reader retrieved", "key", hash)
- return readManifest(manifestReader, hash, fileStore, isEncrypted, quitC)
+ return readManifest(manifestReader, hash, fileStore, isEncrypted, quitC, decrypt)
}
-func readManifest(mr storage.LazySectionReader, hash storage.Address, fileStore *storage.FileStore, isEncrypted bool, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand
+func readManifest(mr storage.LazySectionReader, hash storage.Address, fileStore *storage.FileStore, isEncrypted bool, quitC chan bool, decrypt DecryptFunc) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand
// TODO check size for oversized manifests
size, err := mr.Size(mr.Context(), quitC)
@@ -258,26 +260,41 @@ func readManifest(mr storage.LazySectionReader, hash storage.Address, fileStore
trie = &manifestTrie{
fileStore: fileStore,
encrypted: isEncrypted,
+ decrypt: decrypt,
}
for _, entry := range man.Entries {
- trie.addEntry(entry, quitC)
+ err = trie.addEntry(entry, quitC)
+ if err != nil {
+ return
+ }
}
return
}
-func (mt *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) {
+func (mt *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) error {
mt.ref = nil // trie modified, hash needs to be re-calculated on demand
+ if entry.ManifestEntry.Access != nil {
+ if mt.decrypt == nil {
+ return errors.New("dont have decryptor")
+ }
+
+ err := mt.decrypt(&entry.ManifestEntry)
+ if err != nil {
+ return err
+ }
+ }
+
if len(entry.Path) == 0 {
mt.entries[256] = entry
- return
+ return nil
}
b := entry.Path[0]
oldentry := mt.entries[b]
if (oldentry == nil) || (oldentry.Path == entry.Path && oldentry.ContentType != ManifestType) {
mt.entries[b] = entry
- return
+ return nil
}
cpl := 0
@@ -287,12 +304,12 @@ func (mt *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) {
if (oldentry.ContentType == ManifestType) && (cpl == len(oldentry.Path)) {
if mt.loadSubTrie(oldentry, quitC) != nil {
- return
+ return nil
}
entry.Path = entry.Path[cpl:]
oldentry.subtrie.addEntry(entry, quitC)
oldentry.Hash = ""
- return
+ return nil
}
commonPrefix := entry.Path[:cpl]
@@ -310,6 +327,7 @@ func (mt *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) {
Path: commonPrefix,
ContentType: ManifestType,
}, subtrie)
+ return nil
}
func (mt *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) {
@@ -398,9 +416,20 @@ func (mt *manifestTrie) recalcAndStore() error {
}
func (mt *manifestTrie) loadSubTrie(entry *manifestTrieEntry, quitC chan bool) (err error) {
+ if entry.ManifestEntry.Access != nil {
+ if mt.decrypt == nil {
+ return errors.New("dont have decryptor")
+ }
+
+ err := mt.decrypt(&entry.ManifestEntry)
+ if err != nil {
+ return err
+ }
+ }
+
if entry.subtrie == nil {
hash := common.Hex2Bytes(entry.Hash)
- entry.subtrie, err = loadManifest(context.TODO(), mt.fileStore, hash, quitC)
+ entry.subtrie, err = loadManifest(context.TODO(), mt.fileStore, hash, quitC, mt.decrypt)
entry.Hash = "" // might not match, should be recalculated
}
return
diff --git a/swarm/api/manifest_test.go b/swarm/api/manifest_test.go
index d65f023f8..1c8e53c43 100644
--- a/swarm/api/manifest_test.go
+++ b/swarm/api/manifest_test.go
@@ -44,7 +44,7 @@ func testGetEntry(t *testing.T, path, match string, multiple bool, paths ...stri
quitC := make(chan bool)
fileStore := storage.NewFileStore(nil, storage.NewFileStoreParams())
ref := make([]byte, fileStore.HashSize())
- trie, err := readManifest(manifest(paths...), ref, fileStore, false, quitC)
+ trie, err := readManifest(manifest(paths...), ref, fileStore, false, quitC, NOOPDecrypt)
if err != nil {
t.Errorf("unexpected error making manifest: %v", err)
}
@@ -101,7 +101,7 @@ func TestExactMatch(t *testing.T) {
mf := manifest("shouldBeExactMatch.css", "shouldBeExactMatch.css.map")
fileStore := storage.NewFileStore(nil, storage.NewFileStoreParams())
ref := make([]byte, fileStore.HashSize())
- trie, err := readManifest(mf, ref, fileStore, false, quitC)
+ trie, err := readManifest(mf, ref, fileStore, false, quitC, nil)
if err != nil {
t.Errorf("unexpected error making manifest: %v", err)
}
@@ -134,7 +134,7 @@ func TestAddFileWithManifestPath(t *testing.T) {
}
fileStore := storage.NewFileStore(nil, storage.NewFileStoreParams())
ref := make([]byte, fileStore.HashSize())
- trie, err := readManifest(reader, ref, fileStore, false, nil)
+ trie, err := readManifest(reader, ref, fileStore, false, nil, NOOPDecrypt)
if err != nil {
t.Fatal(err)
}
@@ -161,7 +161,7 @@ func TestReadManifestOverSizeLimit(t *testing.T) {
reader := &storage.LazyTestSectionReader{
SectionReader: io.NewSectionReader(bytes.NewReader(manifest), 0, int64(len(manifest))),
}
- _, err := readManifest(reader, storage.Address{}, nil, false, nil)
+ _, err := readManifest(reader, storage.Address{}, nil, false, nil, NOOPDecrypt)
if err == nil {
t.Fatal("got no error from readManifest")
}
diff --git a/swarm/api/storage.go b/swarm/api/storage.go
index 3b52301a0..8a48fe5bc 100644
--- a/swarm/api/storage.go
+++ b/swarm/api/storage.go
@@ -63,11 +63,11 @@ func (s *Storage) Get(ctx context.Context, bzzpath string) (*Response, error) {
if err != nil {
return nil, err
}
- addr, err := s.api.Resolve(ctx, uri)
+ addr, err := s.api.Resolve(ctx, uri.Addr)
if err != nil {
return nil, err
}
- reader, mimeType, status, _, err := s.api.Get(ctx, addr, uri.Path)
+ reader, mimeType, status, _, err := s.api.Get(ctx, nil, addr, uri.Path)
if err != nil {
return nil, err
}
@@ -93,7 +93,7 @@ func (s *Storage) Modify(ctx context.Context, rootHash, path, contentHash, conte
if err != nil {
return "", err
}
- addr, err := s.api.Resolve(ctx, uri)
+ addr, err := s.api.Resolve(ctx, uri.Addr)
if err != nil {
return "", err
}
diff --git a/swarm/api/uri.go b/swarm/api/uri.go
index 14965e0d9..808517088 100644
--- a/swarm/api/uri.go
+++ b/swarm/api/uri.go
@@ -53,6 +53,19 @@ type URI struct {
Path string
}
+func (u *URI) MarshalJSON() (out []byte, err error) {
+ return []byte(`"` + u.String() + `"`), nil
+}
+
+func (u *URI) UnmarshalJSON(value []byte) error {
+ uri, err := Parse(string(value))
+ if err != nil {
+ return err
+ }
+ *u = *uri
+ return nil
+}
+
// Parse parses rawuri into a URI struct, where rawuri is expected to have one
// of the following formats:
//
diff --git a/swarm/bmt/bmt.go b/swarm/bmt/bmt.go
index 97e0e141e..a85d4369e 100644
--- a/swarm/bmt/bmt.go
+++ b/swarm/bmt/bmt.go
@@ -55,9 +55,6 @@ Two implementations are provided:
*/
const (
- // SegmentCount is the maximum number of segments of the underlying chunk
- // Should be equal to max-chunk-data-size / hash-size
- SegmentCount = 128
// PoolSize is the maximum number of bmt trees used by the hashers, i.e,
// the maximum number of concurrent BMT hashing operations performed by the same hasher
PoolSize = 8
@@ -318,7 +315,7 @@ func (h *Hasher) Sum(b []byte) (s []byte) {
// with every full segment calls writeSection in a go routine
func (h *Hasher) Write(b []byte) (int, error) {
l := len(b)
- if l == 0 || l > 4096 {
+ if l == 0 || l > h.pool.Size {
return 0, nil
}
t := h.getTree()
diff --git a/swarm/bmt/bmt_test.go b/swarm/bmt/bmt_test.go
index 891d8cbb2..760aa11d8 100644
--- a/swarm/bmt/bmt_test.go
+++ b/swarm/bmt/bmt_test.go
@@ -34,6 +34,13 @@ import (
// the actual data length generated (could be longer than max datalength of the BMT)
const BufferSize = 4128
+const (
+ // segmentCount is the maximum number of segments of the underlying chunk
+ // Should be equal to max-chunk-data-size / hash-size
+ // Currently set to 128 == 4096 (default chunk size) / 32 (sha3.keccak256 size)
+ segmentCount = 128
+)
+
var counts = []int{1, 2, 3, 4, 5, 8, 9, 15, 16, 17, 32, 37, 42, 53, 63, 64, 65, 111, 127, 128}
// calculates the Keccak256 SHA3 hash of the data
@@ -224,14 +231,14 @@ func TestHasherReuse(t *testing.T) {
// tests if bmt reuse is not corrupting result
func testHasherReuse(poolsize int, t *testing.T) {
hasher := sha3.NewKeccak256
- pool := NewTreePool(hasher, SegmentCount, poolsize)
+ pool := NewTreePool(hasher, segmentCount, poolsize)
defer pool.Drain(0)
bmt := New(pool)
for i := 0; i < 100; i++ {
data := newData(BufferSize)
n := rand.Intn(bmt.Size())
- err := testHasherCorrectness(bmt, hasher, data, n, SegmentCount)
+ err := testHasherCorrectness(bmt, hasher, data, n, segmentCount)
if err != nil {
t.Fatal(err)
}
@@ -241,7 +248,7 @@ func testHasherReuse(poolsize int, t *testing.T) {
// Tests if pool can be cleanly reused even in concurrent use by several hasher
func TestBMTConcurrentUse(t *testing.T) {
hasher := sha3.NewKeccak256
- pool := NewTreePool(hasher, SegmentCount, PoolSize)
+ pool := NewTreePool(hasher, segmentCount, PoolSize)
defer pool.Drain(0)
cycles := 100
errc := make(chan error)
@@ -451,7 +458,7 @@ func benchmarkBMTBaseline(t *testing.B, n int) {
func benchmarkBMT(t *testing.B, n int) {
data := newData(n)
hasher := sha3.NewKeccak256
- pool := NewTreePool(hasher, SegmentCount, PoolSize)
+ pool := NewTreePool(hasher, segmentCount, PoolSize)
bmt := New(pool)
t.ReportAllocs()
@@ -465,7 +472,7 @@ func benchmarkBMT(t *testing.B, n int) {
func benchmarkBMTAsync(t *testing.B, n int, wh whenHash, double bool) {
data := newData(n)
hasher := sha3.NewKeccak256
- pool := NewTreePool(hasher, SegmentCount, PoolSize)
+ pool := NewTreePool(hasher, segmentCount, PoolSize)
bmt := New(pool).NewAsyncWriter(double)
idxs, segments := splitAndShuffle(bmt.SectionSize(), data)
shuffle(len(idxs), func(i int, j int) {
@@ -483,7 +490,7 @@ func benchmarkBMTAsync(t *testing.B, n int, wh whenHash, double bool) {
func benchmarkPool(t *testing.B, poolsize, n int) {
data := newData(n)
hasher := sha3.NewKeccak256
- pool := NewTreePool(hasher, SegmentCount, poolsize)
+ pool := NewTreePool(hasher, segmentCount, poolsize)
cycles := 100
t.ReportAllocs()
diff --git a/swarm/chunk/chunk.go b/swarm/chunk/chunk.go
new file mode 100644
index 000000000..1449efccd
--- /dev/null
+++ b/swarm/chunk/chunk.go
@@ -0,0 +1,5 @@
+package chunk
+
+const (
+ DefaultSize = 4096
+)
diff --git a/swarm/fuse/swarmfs_test.go b/swarm/fuse/swarmfs_test.go
index d579d15a0..6efeb78d9 100644
--- a/swarm/fuse/swarmfs_test.go
+++ b/swarm/fuse/swarmfs_test.go
@@ -1650,7 +1650,7 @@ func TestFUSE(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- ta := &testAPI{api: api.NewAPI(fileStore, nil, nil)}
+ ta := &testAPI{api: api.NewAPI(fileStore, nil, nil, nil)}
//run a short suite of tests
//approx time: 28s
diff --git a/swarm/network/stream/delivery.go b/swarm/network/stream/delivery.go
index fa210e300..36040339d 100644
--- a/swarm/network/stream/delivery.go
+++ b/swarm/network/stream/delivery.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p/discover"
+ cp "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/log"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/spancontext"
@@ -229,6 +230,11 @@ R:
for req := range d.receiveC {
processReceivedChunksCount.Inc(1)
+ if len(req.SData) > cp.DefaultSize+8 {
+ log.Warn("received chunk is bigger than expected", "len", len(req.SData))
+ continue R
+ }
+
// this should be has locally
chunk, err := d.db.Get(context.TODO(), req.Addr)
if err == nil {
@@ -244,6 +250,7 @@ R:
continue R
default:
}
+
chunk.SData = req.SData
d.db.Put(context.TODO(), chunk)
diff --git a/swarm/network_test.go b/swarm/network_test.go
index d2a030933..176c635d8 100644
--- a/swarm/network_test.go
+++ b/swarm/network_test.go
@@ -445,7 +445,7 @@ func retrieve(
log.Debug("api get: check file", "node", id.String(), "key", f.addr.String(), "total files found", atomic.LoadUint64(totalFoundCount))
- r, _, _, _, err := swarm.api.Get(context.TODO(), f.addr, "/")
+ r, _, _, _, err := swarm.api.Get(context.TODO(), api.NOOPDecrypt, f.addr, "/")
if err != nil {
errc <- fmt.Errorf("api get: node %s, key %s, kademlia %s: %v", id, f.addr, swarm.bzz.Hive, err)
return
diff --git a/swarm/sctx/sctx.go b/swarm/sctx/sctx.go
index 8619f6e19..bed2b1145 100644
--- a/swarm/sctx/sctx.go
+++ b/swarm/sctx/sctx.go
@@ -1,7 +1,22 @@
package sctx
+import "context"
+
type ContextKey int
const (
HTTPRequestIDKey ContextKey = iota
+ requestHostKey
)
+
+func SetHost(ctx context.Context, domain string) context.Context {
+ return context.WithValue(ctx, requestHostKey, domain)
+}
+
+func GetHost(ctx context.Context) string {
+ v, ok := ctx.Value(requestHostKey).(string)
+ if ok {
+ return v
+ }
+ return ""
+}
diff --git a/swarm/storage/chunker.go b/swarm/storage/chunker.go
index b9b502273..6d805b8e2 100644
--- a/swarm/storage/chunker.go
+++ b/swarm/storage/chunker.go
@@ -25,6 +25,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/log"
"github.com/ethereum/go-ethereum/swarm/spancontext"
opentracing "github.com/opentracing/opentracing-go"
@@ -69,10 +70,6 @@ var (
errOperationTimedOut = errors.New("operation timed out")
)
-const (
- DefaultChunkSize int64 = 4096
-)
-
type ChunkerParams struct {
chunkSize int64
hashSize int64
@@ -136,7 +133,7 @@ type TreeChunker struct {
func TreeJoin(ctx context.Context, addr Address, getter Getter, depth int) *LazyChunkReader {
jp := &JoinerParams{
ChunkerParams: ChunkerParams{
- chunkSize: DefaultChunkSize,
+ chunkSize: chunk.DefaultSize,
hashSize: int64(len(addr)),
},
addr: addr,
@@ -156,7 +153,7 @@ func TreeSplit(ctx context.Context, data io.Reader, size int64, putter Putter) (
tsp := &TreeSplitterParams{
SplitterParams: SplitterParams{
ChunkerParams: ChunkerParams{
- chunkSize: DefaultChunkSize,
+ chunkSize: chunk.DefaultSize,
hashSize: putter.RefSize(),
},
reader: data,
diff --git a/swarm/storage/hasherstore.go b/swarm/storage/hasherstore.go
index 139c0ee03..bc23077c1 100644
--- a/swarm/storage/hasherstore.go
+++ b/swarm/storage/hasherstore.go
@@ -22,6 +22,7 @@ import (
"sync"
"github.com/ethereum/go-ethereum/crypto/sha3"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/storage/encryption"
)
@@ -57,7 +58,7 @@ func NewHasherStore(chunkStore ChunkStore, hashFunc SwarmHasher, toEncrypt bool)
refSize := int64(hashSize)
if toEncrypt {
refSize += encryption.KeyLength
- chunkEncryption = newChunkEncryption(DefaultChunkSize, refSize)
+ chunkEncryption = newChunkEncryption(chunk.DefaultSize, refSize)
}
return &hasherStore{
@@ -190,9 +191,9 @@ func (h *hasherStore) decryptChunkData(chunkData ChunkData, encryptionKey encryp
// removing extra bytes which were just added for padding
length := ChunkData(decryptedSpan).Size()
- for length > DefaultChunkSize {
- length = length + (DefaultChunkSize - 1)
- length = length / DefaultChunkSize
+ for length > chunk.DefaultSize {
+ length = length + (chunk.DefaultSize - 1)
+ length = length / chunk.DefaultSize
length *= h.refSize
}
diff --git a/swarm/storage/ldbstore.go b/swarm/storage/ldbstore.go
index 7920ee767..b95aa13b0 100644
--- a/swarm/storage/ldbstore.go
+++ b/swarm/storage/ldbstore.go
@@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/log"
"github.com/ethereum/go-ethereum/swarm/storage/mock"
"github.com/syndtr/goleveldb/leveldb"
@@ -384,14 +385,13 @@ func (s *LDBStore) Import(in io.Reader) (int64, error) {
}
func (s *LDBStore) Cleanup() {
- //Iterates over the database and checks that there are no faulty chunks
+ //Iterates over the database and checks that there are no chunks bigger than 4kb
+ var errorsFound, removed, total int
+
it := s.db.NewIterator()
- startPosition := []byte{keyIndex}
- it.Seek(startPosition)
- var key []byte
- var errorsFound, total int
- for it.Valid() {
- key = it.Key()
+ defer it.Release()
+ for ok := it.Seek([]byte{keyIndex}); ok; ok = it.Next() {
+ key := it.Key()
if (key == nil) || (key[0] != keyIndex) {
break
}
@@ -399,27 +399,50 @@ func (s *LDBStore) Cleanup() {
var index dpaDBIndex
err := decodeIndex(it.Value(), &index)
if err != nil {
- it.Next()
+ log.Warn("Cannot decode")
+ errorsFound++
continue
}
- data, err := s.db.Get(getDataKey(index.Idx, s.po(Address(key[1:]))))
+ hash := key[1:]
+ po := s.po(hash)
+ datakey := getDataKey(index.Idx, po)
+ data, err := s.db.Get(datakey)
if err != nil {
- log.Warn(fmt.Sprintf("Chunk %x found but could not be accessed: %v", key[:], err))
- s.delete(index.Idx, getIndexKey(key[1:]), s.po(Address(key[1:])))
- errorsFound++
- } else {
- hasher := s.hashfunc()
- hasher.Write(data[32:])
- hash := hasher.Sum(nil)
- if !bytes.Equal(hash, key[1:]) {
- log.Warn(fmt.Sprintf("Found invalid chunk. Hash mismatch. hash=%x, key=%x", hash, key[:]))
- s.delete(index.Idx, getIndexKey(key[1:]), s.po(Address(key[1:])))
+ found := false
+
+ // highest possible proximity is 255
+ for po = 1; po <= 255; po++ {
+ datakey = getDataKey(index.Idx, po)
+ data, err = s.db.Get(datakey)
+ if err == nil {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ log.Warn(fmt.Sprintf("Chunk %x found but count not be accessed with any po", key[:]))
+ errorsFound++
+ continue
}
}
- it.Next()
+
+ c := &Chunk{}
+ ck := data[:32]
+ decodeData(data, c)
+
+ cs := int64(binary.LittleEndian.Uint64(c.SData[:8]))
+ log.Trace("chunk", "key", fmt.Sprintf("%x", key[:]), "ck", fmt.Sprintf("%x", ck), "dkey", fmt.Sprintf("%x", datakey), "dataidx", index.Idx, "po", po, "len data", len(data), "len sdata", len(c.SData), "size", cs)
+
+ if len(c.SData) > chunk.DefaultSize+8 {
+ log.Warn("chunk for cleanup", "key", fmt.Sprintf("%x", key[:]), "ck", fmt.Sprintf("%x", ck), "dkey", fmt.Sprintf("%x", datakey), "dataidx", index.Idx, "po", po, "len data", len(data), "len sdata", len(c.SData), "size", cs)
+ s.delete(index.Idx, getIndexKey(key[1:]), po)
+ removed++
+ errorsFound++
+ }
}
- it.Release()
- log.Warn(fmt.Sprintf("Found %v errors out of %v entries", errorsFound, total))
+
+ log.Warn(fmt.Sprintf("Found %v errors out of %v entries. Removed %v chunks.", errorsFound, total, removed))
}
func (s *LDBStore) ReIndex() {
diff --git a/swarm/storage/ldbstore_test.go b/swarm/storage/ldbstore_test.go
index baf9e8c14..5ee88baa5 100644
--- a/swarm/storage/ldbstore_test.go
+++ b/swarm/storage/ldbstore_test.go
@@ -27,6 +27,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/log"
"github.com/ethereum/go-ethereum/swarm/storage/mock/mem"
@@ -184,7 +185,7 @@ func testIterator(t *testing.T, mock bool) {
t.Fatalf("init dbStore failed: %v", err)
}
- chunks := GenerateRandomChunks(DefaultChunkSize, chunkcount)
+ chunks := GenerateRandomChunks(chunk.DefaultSize, chunkcount)
wg := &sync.WaitGroup{}
wg.Add(len(chunks))
@@ -294,7 +295,7 @@ func TestLDBStoreWithoutCollectGarbage(t *testing.T) {
chunks := []*Chunk{}
for i := 0; i < n; i++ {
- c := GenerateRandomChunk(DefaultChunkSize)
+ c := GenerateRandomChunk(chunk.DefaultSize)
chunks = append(chunks, c)
log.Trace("generate random chunk", "idx", i, "chunk", c)
}
@@ -344,7 +345,7 @@ func TestLDBStoreCollectGarbage(t *testing.T) {
chunks := []*Chunk{}
for i := 0; i < n; i++ {
- c := GenerateRandomChunk(DefaultChunkSize)
+ c := GenerateRandomChunk(chunk.DefaultSize)
chunks = append(chunks, c)
log.Trace("generate random chunk", "idx", i, "chunk", c)
}
@@ -398,7 +399,7 @@ func TestLDBStoreAddRemove(t *testing.T) {
chunks := []*Chunk{}
for i := 0; i < n; i++ {
- c := GenerateRandomChunk(DefaultChunkSize)
+ c := GenerateRandomChunk(chunk.DefaultSize)
chunks = append(chunks, c)
log.Trace("generate random chunk", "idx", i, "chunk", c)
}
@@ -460,7 +461,7 @@ func TestLDBStoreRemoveThenCollectGarbage(t *testing.T) {
chunks := []*Chunk{}
for i := 0; i < capacity; i++ {
- c := GenerateRandomChunk(DefaultChunkSize)
+ c := GenerateRandomChunk(chunk.DefaultSize)
chunks = append(chunks, c)
log.Trace("generate random chunk", "idx", i, "chunk", c)
}
diff --git a/swarm/storage/localstore.go b/swarm/storage/localstore.go
index 096d150ae..9e3474979 100644
--- a/swarm/storage/localstore.go
+++ b/swarm/storage/localstore.go
@@ -98,20 +98,16 @@ func NewTestLocalStoreForAddr(params *LocalStoreParams) (*LocalStore, error) {
// After the LDBStore.Put, it is ensured that the MemStore
// contains the chunk with the same data, but nil ReqC channel.
func (ls *LocalStore) Put(ctx context.Context, chunk *Chunk) {
- if l := len(chunk.SData); l < 9 {
- log.Debug("incomplete chunk data", "addr", chunk.Addr, "length", l)
- chunk.SetErrored(ErrChunkInvalid)
- chunk.markAsStored()
- return
- }
valid := true
+ // ls.Validators contains a list of one validator per chunk type.
+ // if one validator succeeds, then the chunk is valid
for _, v := range ls.Validators {
if valid = v.Validate(chunk.Addr, chunk.SData); valid {
break
}
}
if !valid {
- log.Trace("invalid content address", "addr", chunk.Addr)
+ log.Trace("invalid chunk", "addr", chunk.Addr, "len", len(chunk.SData))
chunk.SetErrored(ErrChunkInvalid)
chunk.markAsStored()
return
diff --git a/swarm/storage/localstore_test.go b/swarm/storage/localstore_test.go
index 2bb81efa3..ae62218fe 100644
--- a/swarm/storage/localstore_test.go
+++ b/swarm/storage/localstore_test.go
@@ -20,6 +20,8 @@ import (
"io/ioutil"
"os"
"testing"
+
+ "github.com/ethereum/go-ethereum/swarm/chunk"
)
var (
@@ -61,7 +63,7 @@ func TestValidator(t *testing.T) {
// add content address validator and check puts
// bad should fail, good should pass
store.Validators = append(store.Validators, NewContentAddressValidator(hashfunc))
- chunks = GenerateRandomChunks(DefaultChunkSize, 2)
+ chunks = GenerateRandomChunks(chunk.DefaultSize, 2)
goodChunk = chunks[0]
badChunk = chunks[1]
copy(badChunk.SData, goodChunk.SData)
@@ -79,7 +81,7 @@ func TestValidator(t *testing.T) {
var negV boolTestValidator
store.Validators = append(store.Validators, negV)
- chunks = GenerateRandomChunks(DefaultChunkSize, 2)
+ chunks = GenerateRandomChunks(chunk.DefaultSize, 2)
goodChunk = chunks[0]
badChunk = chunks[1]
copy(badChunk.SData, goodChunk.SData)
@@ -97,7 +99,7 @@ func TestValidator(t *testing.T) {
var posV boolTestValidator = true
store.Validators = append(store.Validators, posV)
- chunks = GenerateRandomChunks(DefaultChunkSize, 2)
+ chunks = GenerateRandomChunks(chunk.DefaultSize, 2)
goodChunk = chunks[0]
badChunk = chunks[1]
copy(badChunk.SData, goodChunk.SData)
diff --git a/swarm/storage/mru/handler.go b/swarm/storage/mru/handler.go
index 32f43d502..57561fd14 100644
--- a/swarm/storage/mru/handler.go
+++ b/swarm/storage/mru/handler.go
@@ -21,17 +21,15 @@ package mru
import (
"bytes"
"context"
- "fmt"
"sync"
"time"
"unsafe"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/log"
"github.com/ethereum/go-ethereum/swarm/storage"
)
-const chunkSize = 4096 // temporary until we implement FileStore in the resourcehandler
-
type Handler struct {
chunkStore *storage.NetStore
HashSize int
@@ -66,8 +64,7 @@ func init() {
}
// NewHandler creates a new Mutable Resource API
-func NewHandler(params *HandlerParams) (*Handler, error) {
-
+func NewHandler(params *HandlerParams) *Handler {
rh := &Handler{
resources: make(map[uint64]*resource),
storeTimeout: defaultStoreTimeout,
@@ -82,7 +79,7 @@ func NewHandler(params *HandlerParams) (*Handler, error) {
hashPool.Put(hashfunc)
}
- return rh, nil
+ return rh
}
// SetStore sets the store backend for the Mutable Resource API
@@ -94,9 +91,8 @@ func (h *Handler) SetStore(store *storage.NetStore) {
// If it looks like a resource update, the chunk address is checked against the ownerAddr of the update's signature
// It implements the storage.ChunkValidator interface
func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool {
-
dataLength := len(data)
- if dataLength < minimumChunkLength {
+ if dataLength < minimumChunkLength || dataLength > chunk.DefaultSize+8 {
return false
}
@@ -106,7 +102,7 @@ func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool {
rootAddr, _ := metadataHash(data)
valid := bytes.Equal(chunkAddr, rootAddr)
if !valid {
- log.Debug(fmt.Sprintf("Invalid root metadata chunk with address: %s", chunkAddr.Hex()))
+ log.Debug("Invalid root metadata chunk with address", "addr", chunkAddr.Hex())
}
return valid
}
@@ -118,7 +114,7 @@ func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool {
// First, deserialize the chunk
var r SignedResourceUpdate
if err := r.fromChunk(chunkAddr, data); err != nil {
- log.Debug("Invalid resource chunk with address %s: %s ", chunkAddr.Hex(), err.Error())
+ log.Debug("Invalid resource chunk", "addr", chunkAddr.Hex(), "err", err.Error())
return false
}
@@ -126,7 +122,7 @@ func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool {
// that was used to retrieve this chunk
// if this validation fails, someone forged a chunk.
if !bytes.Equal(chunkAddr, r.updateHeader.UpdateAddr()) {
- log.Debug("period,version,rootAddr contained in update chunk do not match updateAddr %s", chunkAddr.Hex())
+ log.Debug("period,version,rootAddr contained in update chunk do not match updateAddr", "addr", chunkAddr.Hex())
return false
}
@@ -134,7 +130,7 @@ func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool {
// If it fails, it means either the signature is not valid, data is corrupted
// or someone is trying to update someone else's resource.
if err := r.Verify(); err != nil {
- log.Debug("Invalid signature: %v", err)
+ log.Debug("Invalid signature", "err", err)
return false
}
@@ -172,11 +168,6 @@ func (h *Handler) GetVersion(rootAddr storage.Address) (uint32, error) {
return rsrc.version, nil
}
-// \TODO should be hashsize * branches from the chosen chunker, implement with FileStore
-func (h *Handler) chunkSize() int64 {
- return chunkSize
-}
-
// New creates a new metadata chunk out of the request passed in.
func (h *Handler) New(ctx context.Context, request *Request) error {
diff --git a/swarm/storage/mru/resource_test.go b/swarm/storage/mru/resource_test.go
index 95c9eccdf..76d7c58a1 100644
--- a/swarm/storage/mru/resource_test.go
+++ b/swarm/storage/mru/resource_test.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/contracts/ens"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/multihash"
"github.com/ethereum/go-ethereum/swarm/storage"
)
@@ -776,14 +777,11 @@ func TestValidatorInStore(t *testing.T) {
// set up resource handler and add is as a validator to the localstore
rhParams := &HandlerParams{}
- rh, err := NewHandler(rhParams)
- if err != nil {
- t.Fatal(err)
- }
+ rh := NewHandler(rhParams)
store.Validators = append(store.Validators, rh)
// create content addressed chunks, one good, one faulty
- chunks := storage.GenerateRandomChunks(storage.DefaultChunkSize, 2)
+ chunks := storage.GenerateRandomChunks(chunk.DefaultSize, 2)
goodChunk := chunks[0]
badChunk := chunks[1]
badChunk.SData = goodChunk.SData
diff --git a/swarm/storage/mru/testutil.go b/swarm/storage/mru/testutil.go
index 751f51af3..6efcba9ab 100644
--- a/swarm/storage/mru/testutil.go
+++ b/swarm/storage/mru/testutil.go
@@ -38,10 +38,7 @@ func (t *TestHandler) Close() {
// NewTestHandler creates Handler object to be used for testing purposes.
func NewTestHandler(datadir string, params *HandlerParams) (*TestHandler, error) {
path := filepath.Join(datadir, testDbDirName)
- rh, err := NewHandler(params)
- if err != nil {
- return nil, fmt.Errorf("resource handler create fail: %v", err)
- }
+ rh := NewHandler(params)
localstoreparams := storage.NewDefaultLocalStoreParams()
localstoreparams.Init(path)
localStore, err := storage.NewLocalStore(localstoreparams, nil)
diff --git a/swarm/storage/mru/update.go b/swarm/storage/mru/update.go
index 88c4ac4e5..d1bd37ddf 100644
--- a/swarm/storage/mru/update.go
+++ b/swarm/storage/mru/update.go
@@ -20,6 +20,7 @@ import (
"encoding/binary"
"errors"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/log"
"github.com/ethereum/go-ethereum/swarm/multihash"
)
@@ -42,7 +43,7 @@ const chunkPrefixLength = 2 + 2
//
// Minimum size is Header + 1 (minimum data length, enforced)
const minimumUpdateDataLength = updateHeaderLength + 1
-const maxUpdateDataLength = chunkSize - signatureLength - updateHeaderLength - chunkPrefixLength
+const maxUpdateDataLength = chunk.DefaultSize - signatureLength - updateHeaderLength - chunkPrefixLength
// binaryPut serializes the resource update information into the given slice
func (r *resourceUpdate) binaryPut(serializedData []byte) error {
diff --git a/swarm/storage/pyramid.go b/swarm/storage/pyramid.go
index 2923c81c5..36ff66d04 100644
--- a/swarm/storage/pyramid.go
+++ b/swarm/storage/pyramid.go
@@ -25,6 +25,7 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
"github.com/ethereum/go-ethereum/swarm/log"
)
@@ -101,11 +102,11 @@ func NewPyramidSplitterParams(addr Address, reader io.Reader, putter Putter, get
New chunks to store are store using the putter which the caller provides.
*/
func PyramidSplit(ctx context.Context, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) {
- return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, DefaultChunkSize)).Split(ctx)
+ return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, chunk.DefaultSize)).Split(ctx)
}
func PyramidAppend(ctx context.Context, addr Address, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) {
- return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, DefaultChunkSize)).Append(ctx)
+ return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, chunk.DefaultSize)).Append(ctx)
}
// Entry to create a tree node
diff --git a/swarm/storage/types.go b/swarm/storage/types.go
index 3114ef576..53e3af485 100644
--- a/swarm/storage/types.go
+++ b/swarm/storage/types.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/swarm/bmt"
+ "github.com/ethereum/go-ethereum/swarm/chunk"
)
const MaxPO = 16
@@ -114,7 +115,9 @@ func MakeHashFunc(hash string) SwarmHasher {
case "BMT":
return func() SwarmHash {
hasher := sha3.NewKeccak256
- pool := bmt.NewTreePool(hasher, bmt.SegmentCount, bmt.PoolSize)
+ hasherSize := hasher().Size()
+ segmentCount := chunk.DefaultSize / hasherSize
+ pool := bmt.NewTreePool(hasher, segmentCount, bmt.PoolSize)
return bmt.New(pool)
}
}
@@ -230,8 +233,8 @@ func GenerateRandomChunk(dataSize int64) *Chunk {
func GenerateRandomChunks(dataSize int64, count int) (chunks []*Chunk) {
var i int
hasher := MakeHashFunc(DefaultHash)()
- if dataSize > DefaultChunkSize {
- dataSize = DefaultChunkSize
+ if dataSize > chunk.DefaultSize {
+ dataSize = chunk.DefaultSize
}
for i = 0; i < count; i++ {
@@ -345,6 +348,10 @@ func NewContentAddressValidator(hasher SwarmHasher) *ContentAddressValidator {
// Validate that the given key is a valid content address for the given data
func (v *ContentAddressValidator) Validate(addr Address, data []byte) bool {
+ if l := len(data); l < 9 || l > chunk.DefaultSize+8 {
+ return false
+ }
+
hasher := v.Hasher()
hasher.ResetWithLength(data[:8])
hasher.Write(data[8:])
diff --git a/swarm/swarm.go b/swarm/swarm.go
index c380a376f..a895bdfa5 100644
--- a/swarm/swarm.go
+++ b/swarm/swarm.go
@@ -85,14 +85,12 @@ type Swarm struct {
type SwarmAPI struct {
Api *api.API
Backend chequebook.Backend
- PrvKey *ecdsa.PrivateKey
}
func (self *Swarm) API() *SwarmAPI {
return &SwarmAPI{
Api: self.api,
Backend: self.backend,
- PrvKey: self.privateKey,
}
}
@@ -195,18 +193,13 @@ func NewSwarm(config *api.Config, mockStore *mock.NodeStore) (self *Swarm, err e
var resourceHandler *mru.Handler
rhparams := &mru.HandlerParams{}
- resourceHandler, err = mru.NewHandler(rhparams)
- if err != nil {
- return nil, err
- }
+ resourceHandler = mru.NewHandler(rhparams)
resourceHandler.SetStore(netStore)
- var validators []storage.ChunkValidator
- validators = append(validators, storage.NewContentAddressValidator(storage.MakeHashFunc(storage.DefaultHash)))
- if resourceHandler != nil {
- validators = append(validators, resourceHandler)
+ self.lstore.Validators = []storage.ChunkValidator{
+ storage.NewContentAddressValidator(storage.MakeHashFunc(storage.DefaultHash)),
+ resourceHandler,
}
- self.lstore.Validators = validators
// setup local store
log.Debug(fmt.Sprintf("Set up local storage"))
@@ -222,7 +215,7 @@ func NewSwarm(config *api.Config, mockStore *mock.NodeStore) (self *Swarm, err e
pss.SetHandshakeController(self.ps, pss.NewHandshakeParams())
}
- self.api = api.NewAPI(self.fileStore, self.dns, resourceHandler)
+ self.api = api.NewAPI(self.fileStore, self.dns, resourceHandler, self.privateKey)
// Manifests for Smart Hosting
log.Debug(fmt.Sprintf("-> Web3 virtual server API"))
diff --git a/swarm/testutil/http.go b/swarm/testutil/http.go
index 238f78308..7fd60fcc3 100644
--- a/swarm/testutil/http.go
+++ b/swarm/testutil/http.go
@@ -77,7 +77,7 @@ func NewTestSwarmServer(t *testing.T, serverFunc func(*api.API) TestServer) *Tes
t.Fatal(err)
}
- a := api.NewAPI(fileStore, nil, rh.Handler)
+ a := api.NewAPI(fileStore, nil, rh.Handler, nil)
srv := httptest.NewServer(serverFunc(a))
return &TestSwarmServer{
Server: srv,