github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/api/act.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:42</date>
    10  //</624450111186931712>
    11  
    12  package api
    13  
    14  import (
    15  	"context"
    16  	"crypto/ecdsa"
    17  	"crypto/rand"
    18  	"encoding/hex"
    19  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/crypto"
    28  	"github.com/ethereum/go-ethereum/crypto/ecies"
    29  	"github.com/ethereum/go-ethereum/swarm/log"
    30  	"github.com/ethereum/go-ethereum/swarm/sctx"
    31  	"github.com/ethereum/go-ethereum/swarm/storage"
    32  	"golang.org/x/crypto/scrypt"
    33  	"golang.org/x/crypto/sha3"
    34  	cli "gopkg.in/urfave/cli.v1"
    35  )
    36  
    37  var (
    38  	ErrDecrypt                = errors.New("cant decrypt - forbidden")
    39  	ErrUnknownAccessType      = errors.New("unknown access type (or not implemented)")
    40  	ErrDecryptDomainForbidden = errors.New("decryption request domain forbidden - can only decrypt on localhost")
    41  	AllowedDecryptDomains     = []string{
    42  		"localhost",
    43  		"127.0.0.1",
    44  	}
    45  )
    46  
    47  const EMPTY_CREDENTIALS = ""
    48  
    49  type AccessEntry struct {
    50  	Type      AccessType
    51  	Publisher string
    52  	Salt      []byte
    53  	Act       string
    54  	KdfParams *KdfParams
    55  }
    56  
    57  type DecryptFunc func(*ManifestEntry) error
    58  
    59  func (a *AccessEntry) MarshalJSON() (out []byte, err error) {
    60  
    61  	return json.Marshal(struct {
    62  		Type      AccessType `json:"type,omitempty"`
    63  		Publisher string     `json:"publisher,omitempty"`
    64  		Salt      string     `json:"salt,omitempty"`
    65  		Act       string     `json:"act,omitempty"`
    66  		KdfParams *KdfParams `json:"kdf_params,omitempty"`
    67  	}{
    68  		Type:      a.Type,
    69  		Publisher: a.Publisher,
    70  		Salt:      hex.EncodeToString(a.Salt),
    71  		Act:       a.Act,
    72  		KdfParams: a.KdfParams,
    73  	})
    74  
    75  }
    76  
    77  func (a *AccessEntry) UnmarshalJSON(value []byte) error {
    78  	v := struct {
    79  		Type      AccessType `json:"type,omitempty"`
    80  		Publisher string     `json:"publisher,omitempty"`
    81  		Salt      string     `json:"salt,omitempty"`
    82  		Act       string     `json:"act,omitempty"`
    83  		KdfParams *KdfParams `json:"kdf_params,omitempty"`
    84  	}{}
    85  
    86  	err := json.Unmarshal(value, &v)
    87  	if err != nil {
    88  		return err
    89  	}
    90  	a.Act = v.Act
    91  	a.KdfParams = v.KdfParams
    92  	a.Publisher = v.Publisher
    93  	a.Salt, err = hex.DecodeString(v.Salt)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	if len(a.Salt) != 32 {
    98  		return errors.New("salt should be 32 bytes long")
    99  	}
   100  	a.Type = v.Type
   101  	return nil
   102  }
   103  
   104  type KdfParams struct {
   105  	N int `json:"n"`
   106  	P int `json:"p"`
   107  	R int `json:"r"`
   108  }
   109  
   110  type AccessType string
   111  
   112  const AccessTypePass = AccessType("pass")
   113  const AccessTypePK = AccessType("pk")
   114  const AccessTypeACT = AccessType("act")
   115  
   116  //NewAccessEntryPassword创建清单访问项,以便创建受密码保护的操作
   117  func NewAccessEntryPassword(salt []byte, kdfParams *KdfParams) (*AccessEntry, error) {
   118  	if len(salt) != 32 {
   119  		return nil, fmt.Errorf("salt should be 32 bytes long")
   120  	}
   121  	return &AccessEntry{
   122  		Type:      AccessTypePass,
   123  		Salt:      salt,
   124  		KdfParams: kdfParams,
   125  	}, nil
   126  }
   127  
   128  //NewAccessEntryPk创建一个清单访问条目,以便创建一个由一对椭圆曲线键保护的行为。
   129  func NewAccessEntryPK(publisher string, salt []byte) (*AccessEntry, error) {
   130  	if len(publisher) != 66 {
   131  		return nil, fmt.Errorf("publisher should be 66 characters long, got %d", len(publisher))
   132  	}
   133  	if len(salt) != 32 {
   134  		return nil, fmt.Errorf("salt should be 32 bytes long")
   135  	}
   136  	return &AccessEntry{
   137  		Type:      AccessTypePK,
   138  		Publisher: publisher,
   139  		Salt:      salt,
   140  	}, nil
   141  }
   142  
   143  //newaccessentryat创建一个清单访问条目,以便创建一个由EC密钥和密码组合保护的行为。
   144  func NewAccessEntryACT(publisher string, salt []byte, act string) (*AccessEntry, error) {
   145  	if len(salt) != 32 {
   146  		return nil, fmt.Errorf("salt should be 32 bytes long")
   147  	}
   148  	if len(publisher) != 66 {
   149  		return nil, fmt.Errorf("publisher should be 66 characters long")
   150  	}
   151  
   152  	return &AccessEntry{
   153  		Type:      AccessTypeACT,
   154  		Publisher: publisher,
   155  		Salt:      salt,
   156  		Act:       act,
   157  		KdfParams: DefaultKdfParams,
   158  	}, nil
   159  }
   160  
   161  //noopdecrypt是一个通用的解密函数,在真正的行为解密功能所在的地方传递给API。
   162  //或者不需要,或者不能在即时范围内实现
   163  func NOOPDecrypt(*ManifestEntry) error {
   164  	return nil
   165  }
   166  
   167  var DefaultKdfParams = NewKdfParams(262144, 1, 8)
   168  
   169  //newkdfarams返回具有给定scrypt参数的kdfarams结构
   170  func NewKdfParams(n, p, r int) *KdfParams {
   171  
   172  	return &KdfParams{
   173  		N: n,
   174  		P: p,
   175  		R: r,
   176  	}
   177  }
   178  
   179  //newsessionkeypassword基于共享机密(密码)和给定的salt创建会话密钥
   180  //和访问项中的kdf参数
   181  func NewSessionKeyPassword(password string, accessEntry *AccessEntry) ([]byte, error) {
   182  	if accessEntry.Type != AccessTypePass && accessEntry.Type != AccessTypeACT {
   183  		return nil, errors.New("incorrect access entry type")
   184  
   185  	}
   186  	return sessionKeyPassword(password, accessEntry.Salt, accessEntry.KdfParams)
   187  }
   188  
   189  func sessionKeyPassword(password string, salt []byte, kdfParams *KdfParams) ([]byte, error) {
   190  	return scrypt.Key(
   191  		[]byte(password),
   192  		salt,
   193  		kdfParams.N,
   194  		kdfParams.R,
   195  		kdfParams.P,
   196  		32,
   197  	)
   198  }
   199  
   200  //newsessionkeypk为给定的密钥对和给定的salt值使用ECDH共享机密创建新的act会话密钥
   201  func NewSessionKeyPK(private *ecdsa.PrivateKey, public *ecdsa.PublicKey, salt []byte) ([]byte, error) {
   202  	granteePubEcies := ecies.ImportECDSAPublic(public)
   203  	privateKey := ecies.ImportECDSA(private)
   204  
   205  	bytes, err := privateKey.GenerateShared(granteePubEcies, 16, 16)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	bytes = append(salt, bytes...)
   210  	sessionKey := crypto.Keccak256(bytes)
   211  	return sessionKey, nil
   212  }
   213  
   214  func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.PrivateKey) DecryptFunc {
   215  	return func(m *ManifestEntry) error {
   216  		if m.Access == nil {
   217  			return nil
   218  		}
   219  
   220  		allowed := false
   221  		requestDomain := sctx.GetHost(ctx)
   222  		for _, v := range AllowedDecryptDomains {
   223  			if strings.Contains(requestDomain, v) {
   224  				allowed = true
   225  			}
   226  		}
   227  
   228  		if !allowed {
   229  			return ErrDecryptDomainForbidden
   230  		}
   231  
   232  		switch m.Access.Type {
   233  		case "pass":
   234  			if credentials != "" {
   235  				key, err := NewSessionKeyPassword(credentials, m.Access)
   236  				if err != nil {
   237  					return err
   238  				}
   239  
   240  				ref, err := hex.DecodeString(m.Hash)
   241  				if err != nil {
   242  					return err
   243  				}
   244  
   245  				enc := NewRefEncryption(len(ref) - 8)
   246  				decodedRef, err := enc.Decrypt(ref, key)
   247  				if err != nil {
   248  					return ErrDecrypt
   249  				}
   250  
   251  				m.Hash = hex.EncodeToString(decodedRef)
   252  				m.Access = nil
   253  				return nil
   254  			}
   255  			return ErrDecrypt
   256  		case "pk":
   257  			publisherBytes, err := hex.DecodeString(m.Access.Publisher)
   258  			if err != nil {
   259  				return ErrDecrypt
   260  			}
   261  			publisher, err := crypto.DecompressPubkey(publisherBytes)
   262  			if err != nil {
   263  				return ErrDecrypt
   264  			}
   265  			key, err := NewSessionKeyPK(pk, publisher, m.Access.Salt)
   266  			if err != nil {
   267  				return ErrDecrypt
   268  			}
   269  			ref, err := hex.DecodeString(m.Hash)
   270  			if err != nil {
   271  				return err
   272  			}
   273  
   274  			enc := NewRefEncryption(len(ref) - 8)
   275  			decodedRef, err := enc.Decrypt(ref, key)
   276  			if err != nil {
   277  				return ErrDecrypt
   278  			}
   279  
   280  			m.Hash = hex.EncodeToString(decodedRef)
   281  			m.Access = nil
   282  			return nil
   283  		case "act":
   284  			var (
   285  				sessionKey []byte
   286  				err        error
   287  			)
   288  
   289  			publisherBytes, err := hex.DecodeString(m.Access.Publisher)
   290  			if err != nil {
   291  				return ErrDecrypt
   292  			}
   293  			publisher, err := crypto.DecompressPubkey(publisherBytes)
   294  			if err != nil {
   295  				return ErrDecrypt
   296  			}
   297  
   298  			sessionKey, err = NewSessionKeyPK(pk, publisher, m.Access.Salt)
   299  			if err != nil {
   300  				return ErrDecrypt
   301  			}
   302  
   303  			found, ciphertext, decryptionKey, err := a.getACTDecryptionKey(ctx, storage.Address(common.Hex2Bytes(m.Access.Act)), sessionKey)
   304  			if err != nil {
   305  				return err
   306  			}
   307  			if !found {
   308  //尝试返回到密码
   309  				if credentials != "" {
   310  					sessionKey, err = NewSessionKeyPassword(credentials, m.Access)
   311  					if err != nil {
   312  						return err
   313  					}
   314  					found, ciphertext, decryptionKey, err = a.getACTDecryptionKey(ctx, storage.Address(common.Hex2Bytes(m.Access.Act)), sessionKey)
   315  					if err != nil {
   316  						return err
   317  					}
   318  					if !found {
   319  						return ErrDecrypt
   320  					}
   321  				} else {
   322  					return ErrDecrypt
   323  				}
   324  			}
   325  			enc := NewRefEncryption(len(ciphertext) - 8)
   326  			decodedRef, err := enc.Decrypt(ciphertext, decryptionKey)
   327  			if err != nil {
   328  				return ErrDecrypt
   329  			}
   330  
   331  			ref, err := hex.DecodeString(m.Hash)
   332  			if err != nil {
   333  				return err
   334  			}
   335  
   336  			enc = NewRefEncryption(len(ref) - 8)
   337  			decodedMainRef, err := enc.Decrypt(ref, decodedRef)
   338  			if err != nil {
   339  				return ErrDecrypt
   340  			}
   341  			m.Hash = hex.EncodeToString(decodedMainRef)
   342  			m.Access = nil
   343  			return nil
   344  		}
   345  		return ErrUnknownAccessType
   346  	}
   347  }
   348  
   349  func (a *API) getACTDecryptionKey(ctx context.Context, actManifestAddress storage.Address, sessionKey []byte) (found bool, ciphertext, decryptionKey []byte, err error) {
   350  	hasher := sha3.NewLegacyKeccak256()
   351  	hasher.Write(append(sessionKey, 0))
   352  	lookupKey := hasher.Sum(nil)
   353  	hasher.Reset()
   354  
   355  	hasher.Write(append(sessionKey, 1))
   356  	accessKeyDecryptionKey := hasher.Sum(nil)
   357  	hasher.Reset()
   358  
   359  	lk := hex.EncodeToString(lookupKey)
   360  	list, err := a.GetManifestList(ctx, NOOPDecrypt, actManifestAddress, lk)
   361  	if err != nil {
   362  		return false, nil, nil, err
   363  	}
   364  	for _, v := range list.Entries {
   365  		if v.Path == lk {
   366  			cipherTextBytes, err := hex.DecodeString(v.Hash)
   367  			if err != nil {
   368  				return false, nil, nil, err
   369  			}
   370  			return true, cipherTextBytes, accessKeyDecryptionKey, nil
   371  		}
   372  	}
   373  	return false, nil, nil, nil
   374  }
   375  
   376  func GenerateAccessControlManifest(ctx *cli.Context, ref string, accessKey []byte, ae *AccessEntry) (*Manifest, error) {
   377  	refBytes, err := hex.DecodeString(ref)
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  //用accesskey加密ref
   382  	enc := NewRefEncryption(len(refBytes))
   383  	encrypted, err := enc.Encrypt(refBytes, accessKey)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  
   388  	m := &Manifest{
   389  		Entries: []ManifestEntry{
   390  			{
   391  				Hash:        hex.EncodeToString(encrypted),
   392  				ContentType: ManifestType,
   393  				ModTime:     time.Now(),
   394  				Access:      ae,
   395  			},
   396  		},
   397  	}
   398  
   399  	return m, nil
   400  }
   401  
   402  //dopk是cli api的一个助手函数,用于处理
   403  //根据cli上下文、ec键和salt创建会话键和访问项
   404  func DoPK(ctx *cli.Context, privateKey *ecdsa.PrivateKey, granteePublicKey string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) {
   405  	if granteePublicKey == "" {
   406  		return nil, nil, errors.New("need a grantee Public Key")
   407  	}
   408  	b, err := hex.DecodeString(granteePublicKey)
   409  	if err != nil {
   410  		log.Error("error decoding grantee public key", "err", err)
   411  		return nil, nil, err
   412  	}
   413  
   414  	granteePub, err := crypto.DecompressPubkey(b)
   415  	if err != nil {
   416  		log.Error("error decompressing grantee public key", "err", err)
   417  		return nil, nil, err
   418  	}
   419  
   420  	sessionKey, err = NewSessionKeyPK(privateKey, granteePub, salt)
   421  	if err != nil {
   422  		log.Error("error getting session key", "err", err)
   423  		return nil, nil, err
   424  	}
   425  
   426  	ae, err = NewAccessEntryPK(hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)), salt)
   427  	if err != nil {
   428  		log.Error("error generating access entry", "err", err)
   429  		return nil, nil, err
   430  	}
   431  
   432  	return sessionKey, ae, nil
   433  }
   434  
   435  //doact是cli api的一个助手函数,用于处理
   436  //在给定cli上下文、ec密钥、密码授予者和salt的情况下,创建访问密钥、访问条目和行为清单(包括上载它)
   437  func DoACT(ctx *cli.Context, privateKey *ecdsa.PrivateKey, salt []byte, grantees []string, encryptPasswords []string) (accessKey []byte, ae *AccessEntry, actManifest *Manifest, err error) {
   438  	if len(grantees) == 0 && len(encryptPasswords) == 0 {
   439  		return nil, nil, nil, errors.New("did not get any grantee public keys or any encryption passwords")
   440  	}
   441  
   442  	publisherPub := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
   443  	grantees = append(grantees, publisherPub)
   444  
   445  	accessKey = make([]byte, 32)
   446  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   447  		panic("reading from crypto/rand failed: " + err.Error())
   448  	}
   449  	if _, err := io.ReadFull(rand.Reader, accessKey); err != nil {
   450  		panic("reading from crypto/rand failed: " + err.Error())
   451  	}
   452  
   453  	lookupPathEncryptedAccessKeyMap := make(map[string]string)
   454  	i := 0
   455  	for _, v := range grantees {
   456  		i++
   457  		if v == "" {
   458  			return nil, nil, nil, errors.New("need a grantee Public Key")
   459  		}
   460  		b, err := hex.DecodeString(v)
   461  		if err != nil {
   462  			log.Error("error decoding grantee public key", "err", err)
   463  			return nil, nil, nil, err
   464  		}
   465  
   466  		granteePub, err := crypto.DecompressPubkey(b)
   467  		if err != nil {
   468  			log.Error("error decompressing grantee public key", "err", err)
   469  			return nil, nil, nil, err
   470  		}
   471  		sessionKey, err := NewSessionKeyPK(privateKey, granteePub, salt)
   472  		if err != nil {
   473  			return nil, nil, nil, err
   474  		}
   475  
   476  		hasher := sha3.NewLegacyKeccak256()
   477  		hasher.Write(append(sessionKey, 0))
   478  		lookupKey := hasher.Sum(nil)
   479  
   480  		hasher.Reset()
   481  		hasher.Write(append(sessionKey, 1))
   482  
   483  		accessKeyEncryptionKey := hasher.Sum(nil)
   484  
   485  		enc := NewRefEncryption(len(accessKey))
   486  		encryptedAccessKey, err := enc.Encrypt(accessKey, accessKeyEncryptionKey)
   487  		if err != nil {
   488  			return nil, nil, nil, err
   489  		}
   490  		lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey)
   491  	}
   492  
   493  	for _, pass := range encryptPasswords {
   494  		sessionKey, err := sessionKeyPassword(pass, salt, DefaultKdfParams)
   495  		if err != nil {
   496  			return nil, nil, nil, err
   497  		}
   498  		hasher := sha3.NewLegacyKeccak256()
   499  		hasher.Write(append(sessionKey, 0))
   500  		lookupKey := hasher.Sum(nil)
   501  
   502  		hasher.Reset()
   503  		hasher.Write(append(sessionKey, 1))
   504  
   505  		accessKeyEncryptionKey := hasher.Sum(nil)
   506  
   507  		enc := NewRefEncryption(len(accessKey))
   508  		encryptedAccessKey, err := enc.Encrypt(accessKey, accessKeyEncryptionKey)
   509  		if err != nil {
   510  			return nil, nil, nil, err
   511  		}
   512  		lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey)
   513  	}
   514  
   515  	m := &Manifest{
   516  		Entries: []ManifestEntry{},
   517  	}
   518  
   519  	for k, v := range lookupPathEncryptedAccessKeyMap {
   520  		m.Entries = append(m.Entries, ManifestEntry{
   521  			Path:        k,
   522  			Hash:        v,
   523  			ContentType: "text/plain",
   524  		})
   525  	}
   526  
   527  	ae, err = NewAccessEntryACT(hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)), salt, "")
   528  	if err != nil {
   529  		return nil, nil, nil, err
   530  	}
   531  
   532  	return accessKey, ae, m, nil
   533  }
   534  
   535  //dopassword是cli api的一个助手函数,用于处理
   536  //根据cli上下文、密码和salt创建会话密钥和访问条目。
   537  //默认情况下-DefaultKdfParams用作scrypt参数
   538  func DoPassword(ctx *cli.Context, password string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) {
   539  	ae, err = NewAccessEntryPassword(salt, DefaultKdfParams)
   540  	if err != nil {
   541  		return nil, nil, err
   542  	}
   543  
   544  	sessionKey, err = NewSessionKeyPassword(password, ae)
   545  	if err != nil {
   546  		return nil, nil, err
   547  	}
   548  	return sessionKey, ae, nil
   549  }
   550