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