github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/swarm/api/act.go (about) 1 package api 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "crypto/rand" 7 "encoding/hex" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "io" 12 "strings" 13 "time" 14 15 "github.com/PlatONnetwork/PlatON-Go/common" 16 "github.com/PlatONnetwork/PlatON-Go/crypto" 17 "github.com/PlatONnetwork/PlatON-Go/crypto/ecies" 18 "github.com/PlatONnetwork/PlatON-Go/crypto/sha3" 19 "github.com/PlatONnetwork/PlatON-Go/swarm/log" 20 "github.com/PlatONnetwork/PlatON-Go/swarm/sctx" 21 "github.com/PlatONnetwork/PlatON-Go/swarm/storage" 22 "golang.org/x/crypto/scrypt" 23 cli "gopkg.in/urfave/cli.v1" 24 ) 25 26 var ( 27 ErrDecrypt = errors.New("cant decrypt - forbidden") 28 ErrUnknownAccessType = errors.New("unknown access type (or not implemented)") 29 ErrDecryptDomainForbidden = errors.New("decryption request domain forbidden - can only decrypt on localhost") 30 AllowedDecryptDomains = []string{ 31 "localhost", 32 "127.0.0.1", 33 } 34 ) 35 36 const EMPTY_CREDENTIALS = "" 37 38 type AccessEntry struct { 39 Type AccessType 40 Publisher string 41 Salt []byte 42 Act string 43 KdfParams *KdfParams 44 } 45 46 type DecryptFunc func(*ManifestEntry) error 47 48 func (a *AccessEntry) MarshalJSON() (out []byte, err error) { 49 50 return json.Marshal(struct { 51 Type AccessType `json:"type,omitempty"` 52 Publisher string `json:"publisher,omitempty"` 53 Salt string `json:"salt,omitempty"` 54 Act string `json:"act,omitempty"` 55 KdfParams *KdfParams `json:"kdf_params,omitempty"` 56 }{ 57 Type: a.Type, 58 Publisher: a.Publisher, 59 Salt: hex.EncodeToString(a.Salt), 60 Act: a.Act, 61 KdfParams: a.KdfParams, 62 }) 63 64 } 65 66 func (a *AccessEntry) UnmarshalJSON(value []byte) error { 67 v := struct { 68 Type AccessType `json:"type,omitempty"` 69 Publisher string `json:"publisher,omitempty"` 70 Salt string `json:"salt,omitempty"` 71 Act string `json:"act,omitempty"` 72 KdfParams *KdfParams `json:"kdf_params,omitempty"` 73 }{} 74 75 err := json.Unmarshal(value, &v) 76 if err != nil { 77 return err 78 } 79 a.Act = v.Act 80 a.KdfParams = v.KdfParams 81 a.Publisher = v.Publisher 82 a.Salt, err = hex.DecodeString(v.Salt) 83 if err != nil { 84 return err 85 } 86 if len(a.Salt) != 32 { 87 return errors.New("salt should be 32 bytes long") 88 } 89 a.Type = v.Type 90 return nil 91 } 92 93 type KdfParams struct { 94 N int `json:"n"` 95 P int `json:"p"` 96 R int `json:"r"` 97 } 98 99 type AccessType string 100 101 const AccessTypePass = AccessType("pass") 102 const AccessTypePK = AccessType("pk") 103 const AccessTypeACT = AccessType("act") 104 105 // NewAccessEntryPassword creates a manifest AccessEntry in order to create an ACT protected by a password 106 func NewAccessEntryPassword(salt []byte, kdfParams *KdfParams) (*AccessEntry, error) { 107 if len(salt) != 32 { 108 return nil, fmt.Errorf("salt should be 32 bytes long") 109 } 110 return &AccessEntry{ 111 Type: AccessTypePass, 112 Salt: salt, 113 KdfParams: kdfParams, 114 }, nil 115 } 116 117 // NewAccessEntryPK creates a manifest AccessEntry in order to create an ACT protected by a pair of Elliptic Curve keys 118 func NewAccessEntryPK(publisher string, salt []byte) (*AccessEntry, error) { 119 if len(publisher) != 66 { 120 return nil, fmt.Errorf("publisher should be 66 characters long, got %d", len(publisher)) 121 } 122 if len(salt) != 32 { 123 return nil, fmt.Errorf("salt should be 32 bytes long") 124 } 125 return &AccessEntry{ 126 Type: AccessTypePK, 127 Publisher: publisher, 128 Salt: salt, 129 }, nil 130 } 131 132 // NewAccessEntryACT creates a manifest AccessEntry in order to create an ACT protected by a combination of EC keys and passwords 133 func NewAccessEntryACT(publisher string, salt []byte, act string) (*AccessEntry, error) { 134 if len(salt) != 32 { 135 return nil, fmt.Errorf("salt should be 32 bytes long") 136 } 137 if len(publisher) != 66 { 138 return nil, fmt.Errorf("publisher should be 66 characters long") 139 } 140 141 return &AccessEntry{ 142 Type: AccessTypeACT, 143 Publisher: publisher, 144 Salt: salt, 145 Act: act, 146 KdfParams: DefaultKdfParams, 147 }, nil 148 } 149 150 // NOOPDecrypt is a generic decrypt function that is passed into the API in places where real ACT decryption capabilities are 151 // either unwanted, or alternatively, cannot be implemented in the immediate scope 152 func NOOPDecrypt(*ManifestEntry) error { 153 return nil 154 } 155 156 var DefaultKdfParams = NewKdfParams(262144, 1, 8) 157 158 // NewKdfParams returns a KdfParams struct with the given scrypt params 159 func NewKdfParams(n, p, r int) *KdfParams { 160 161 return &KdfParams{ 162 N: n, 163 P: p, 164 R: r, 165 } 166 } 167 168 // NewSessionKeyPassword creates a session key based on a shared secret (password) and the given salt 169 // and kdf parameters in the access entry 170 func NewSessionKeyPassword(password string, accessEntry *AccessEntry) ([]byte, error) { 171 if accessEntry.Type != AccessTypePass && accessEntry.Type != AccessTypeACT { 172 return nil, errors.New("incorrect access entry type") 173 174 } 175 return sessionKeyPassword(password, accessEntry.Salt, accessEntry.KdfParams) 176 } 177 178 func sessionKeyPassword(password string, salt []byte, kdfParams *KdfParams) ([]byte, error) { 179 return scrypt.Key( 180 []byte(password), 181 salt, 182 kdfParams.N, 183 kdfParams.R, 184 kdfParams.P, 185 32, 186 ) 187 } 188 189 // NewSessionKeyPK creates a new ACT Session Key using an ECDH shared secret for the given key pair and the given salt value 190 func NewSessionKeyPK(private *ecdsa.PrivateKey, public *ecdsa.PublicKey, salt []byte) ([]byte, error) { 191 granteePubEcies := ecies.ImportECDSAPublic(public) 192 privateKey := ecies.ImportECDSA(private) 193 194 bytes, err := privateKey.GenerateShared(granteePubEcies, 16, 16) 195 if err != nil { 196 return nil, err 197 } 198 bytes = append(salt, bytes...) 199 sessionKey := crypto.Keccak256(bytes) 200 return sessionKey, nil 201 } 202 203 func (a *API) doDecrypt(ctx context.Context, credentials string, pk *ecdsa.PrivateKey) DecryptFunc { 204 return func(m *ManifestEntry) error { 205 if m.Access == nil { 206 return nil 207 } 208 209 allowed := false 210 requestDomain := sctx.GetHost(ctx) 211 for _, v := range AllowedDecryptDomains { 212 if strings.Contains(requestDomain, v) { 213 allowed = true 214 } 215 } 216 217 if !allowed { 218 return ErrDecryptDomainForbidden 219 } 220 221 switch m.Access.Type { 222 case "pass": 223 if credentials != "" { 224 key, err := NewSessionKeyPassword(credentials, m.Access) 225 if err != nil { 226 return err 227 } 228 229 ref, err := hex.DecodeString(m.Hash) 230 if err != nil { 231 return err 232 } 233 234 enc := NewRefEncryption(len(ref) - 8) 235 decodedRef, err := enc.Decrypt(ref, key) 236 if err != nil { 237 return ErrDecrypt 238 } 239 240 m.Hash = hex.EncodeToString(decodedRef) 241 m.Access = nil 242 return nil 243 } 244 return ErrDecrypt 245 case "pk": 246 publisherBytes, err := hex.DecodeString(m.Access.Publisher) 247 if err != nil { 248 return ErrDecrypt 249 } 250 publisher, err := crypto.DecompressPubkey(publisherBytes) 251 if err != nil { 252 return ErrDecrypt 253 } 254 key, err := NewSessionKeyPK(pk, publisher, m.Access.Salt) 255 if err != nil { 256 return ErrDecrypt 257 } 258 ref, err := hex.DecodeString(m.Hash) 259 if err != nil { 260 return err 261 } 262 263 enc := NewRefEncryption(len(ref) - 8) 264 decodedRef, err := enc.Decrypt(ref, key) 265 if err != nil { 266 return ErrDecrypt 267 } 268 269 m.Hash = hex.EncodeToString(decodedRef) 270 m.Access = nil 271 return nil 272 case "act": 273 var ( 274 sessionKey []byte 275 err error 276 ) 277 278 publisherBytes, err := hex.DecodeString(m.Access.Publisher) 279 if err != nil { 280 return ErrDecrypt 281 } 282 publisher, err := crypto.DecompressPubkey(publisherBytes) 283 if err != nil { 284 return ErrDecrypt 285 } 286 287 sessionKey, err = NewSessionKeyPK(pk, publisher, m.Access.Salt) 288 if err != nil { 289 return ErrDecrypt 290 } 291 292 found, ciphertext, decryptionKey, err := a.getACTDecryptionKey(ctx, storage.Address(common.Hex2Bytes(m.Access.Act)), sessionKey) 293 if err != nil { 294 return err 295 } 296 if !found { 297 // try to fall back to password 298 if credentials != "" { 299 sessionKey, err = NewSessionKeyPassword(credentials, m.Access) 300 if err != nil { 301 return err 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 return ErrDecrypt 309 } 310 } else { 311 return ErrDecrypt 312 } 313 } 314 enc := NewRefEncryption(len(ciphertext) - 8) 315 decodedRef, err := enc.Decrypt(ciphertext, decryptionKey) 316 if err != nil { 317 return ErrDecrypt 318 } 319 320 ref, err := hex.DecodeString(m.Hash) 321 if err != nil { 322 return err 323 } 324 325 enc = NewRefEncryption(len(ref) - 8) 326 decodedMainRef, err := enc.Decrypt(ref, decodedRef) 327 if err != nil { 328 return ErrDecrypt 329 } 330 m.Hash = hex.EncodeToString(decodedMainRef) 331 m.Access = nil 332 return nil 333 } 334 return ErrUnknownAccessType 335 } 336 } 337 338 func (a *API) getACTDecryptionKey(ctx context.Context, actManifestAddress storage.Address, sessionKey []byte) (found bool, ciphertext, decryptionKey []byte, err error) { 339 hasher := sha3.NewKeccak256() 340 hasher.Write(append(sessionKey, 0)) 341 lookupKey := hasher.Sum(nil) 342 hasher.Reset() 343 344 hasher.Write(append(sessionKey, 1)) 345 accessKeyDecryptionKey := hasher.Sum(nil) 346 hasher.Reset() 347 348 lk := hex.EncodeToString(lookupKey) 349 list, err := a.GetManifestList(ctx, NOOPDecrypt, actManifestAddress, lk) 350 if err != nil { 351 return false, nil, nil, err 352 } 353 for _, v := range list.Entries { 354 if v.Path == lk { 355 cipherTextBytes, err := hex.DecodeString(v.Hash) 356 if err != nil { 357 return false, nil, nil, err 358 } 359 return true, cipherTextBytes, accessKeyDecryptionKey, nil 360 } 361 } 362 return false, nil, nil, nil 363 } 364 365 func GenerateAccessControlManifest(ctx *cli.Context, ref string, accessKey []byte, ae *AccessEntry) (*Manifest, error) { 366 refBytes, err := hex.DecodeString(ref) 367 if err != nil { 368 return nil, err 369 } 370 // encrypt ref with accessKey 371 enc := NewRefEncryption(len(refBytes)) 372 encrypted, err := enc.Encrypt(refBytes, accessKey) 373 if err != nil { 374 return nil, err 375 } 376 377 m := &Manifest{ 378 Entries: []ManifestEntry{ 379 { 380 Hash: hex.EncodeToString(encrypted), 381 ContentType: ManifestType, 382 ModTime: time.Now(), 383 Access: ae, 384 }, 385 }, 386 } 387 388 return m, nil 389 } 390 391 // DoPK is a helper function to the CLI API that handles the entire business logic for 392 // creating a session key and access entry given the cli context, ec keys and salt 393 func DoPK(ctx *cli.Context, privateKey *ecdsa.PrivateKey, granteePublicKey string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) { 394 if granteePublicKey == "" { 395 return nil, nil, errors.New("need a grantee Public Key") 396 } 397 b, err := hex.DecodeString(granteePublicKey) 398 if err != nil { 399 log.Error("error decoding grantee public key", "err", err) 400 return nil, nil, err 401 } 402 403 granteePub, err := crypto.DecompressPubkey(b) 404 if err != nil { 405 log.Error("error decompressing grantee public key", "err", err) 406 return nil, nil, err 407 } 408 409 sessionKey, err = NewSessionKeyPK(privateKey, granteePub, salt) 410 if err != nil { 411 log.Error("error getting session key", "err", err) 412 return nil, nil, err 413 } 414 415 ae, err = NewAccessEntryPK(hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)), salt) 416 if err != nil { 417 log.Error("error generating access entry", "err", err) 418 return nil, nil, err 419 } 420 421 return sessionKey, ae, nil 422 } 423 424 // DoACT is a helper function to the CLI API that handles the entire business logic for 425 // creating a access key, access entry and ACT manifest (including uploading it) given the cli context, ec keys, password grantees and salt 426 func DoACT(ctx *cli.Context, privateKey *ecdsa.PrivateKey, salt []byte, grantees []string, encryptPasswords []string) (accessKey []byte, ae *AccessEntry, actManifest *Manifest, err error) { 427 if len(grantees) == 0 && len(encryptPasswords) == 0 { 428 return nil, nil, nil, errors.New("did not get any grantee public keys or any encryption passwords") 429 } 430 431 publisherPub := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)) 432 grantees = append(grantees, publisherPub) 433 434 accessKey = make([]byte, 32) 435 if _, err := io.ReadFull(rand.Reader, salt); err != nil { 436 panic("reading from crypto/rand failed: " + err.Error()) 437 } 438 if _, err := io.ReadFull(rand.Reader, accessKey); err != nil { 439 panic("reading from crypto/rand failed: " + err.Error()) 440 } 441 442 lookupPathEncryptedAccessKeyMap := make(map[string]string) 443 i := 0 444 for _, v := range grantees { 445 i++ 446 if v == "" { 447 return nil, nil, nil, errors.New("need a grantee Public Key") 448 } 449 b, err := hex.DecodeString(v) 450 if err != nil { 451 log.Error("error decoding grantee public key", "err", err) 452 return nil, nil, nil, err 453 } 454 455 granteePub, err := crypto.DecompressPubkey(b) 456 if err != nil { 457 log.Error("error decompressing grantee public key", "err", err) 458 return nil, nil, nil, err 459 } 460 sessionKey, err := NewSessionKeyPK(privateKey, granteePub, salt) 461 462 hasher := sha3.NewKeccak256() 463 hasher.Write(append(sessionKey, 0)) 464 lookupKey := hasher.Sum(nil) 465 466 hasher.Reset() 467 hasher.Write(append(sessionKey, 1)) 468 469 accessKeyEncryptionKey := hasher.Sum(nil) 470 471 enc := NewRefEncryption(len(accessKey)) 472 encryptedAccessKey, err := enc.Encrypt(accessKey, accessKeyEncryptionKey) 473 if err != nil { 474 return nil, nil, nil, err 475 } 476 lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey) 477 } 478 479 for _, pass := range encryptPasswords { 480 sessionKey, err := sessionKeyPassword(pass, salt, DefaultKdfParams) 481 if err != nil { 482 return nil, nil, nil, err 483 } 484 hasher := sha3.NewKeccak256() 485 hasher.Write(append(sessionKey, 0)) 486 lookupKey := hasher.Sum(nil) 487 488 hasher.Reset() 489 hasher.Write(append(sessionKey, 1)) 490 491 accessKeyEncryptionKey := hasher.Sum(nil) 492 493 enc := NewRefEncryption(len(accessKey)) 494 encryptedAccessKey, err := enc.Encrypt(accessKey, accessKeyEncryptionKey) 495 if err != nil { 496 return nil, nil, nil, err 497 } 498 lookupPathEncryptedAccessKeyMap[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey) 499 } 500 501 m := &Manifest{ 502 Entries: []ManifestEntry{}, 503 } 504 505 for k, v := range lookupPathEncryptedAccessKeyMap { 506 m.Entries = append(m.Entries, ManifestEntry{ 507 Path: k, 508 Hash: v, 509 ContentType: "text/plain", 510 }) 511 } 512 513 ae, err = NewAccessEntryACT(hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)), salt, "") 514 if err != nil { 515 return nil, nil, nil, err 516 } 517 518 return accessKey, ae, m, nil 519 } 520 521 // DoPassword is a helper function to the CLI API that handles the entire business logic for 522 // creating a session key and an access entry given the cli context, password and salt. 523 // By default - DefaultKdfParams are used as the scrypt params 524 func DoPassword(ctx *cli.Context, password string, salt []byte) (sessionKey []byte, ae *AccessEntry, err error) { 525 ae, err = NewAccessEntryPassword(salt, DefaultKdfParams) 526 if err != nil { 527 return nil, nil, err 528 } 529 530 sessionKey, err = NewSessionKeyPassword(password, ae) 531 if err != nil { 532 return nil, nil, err 533 } 534 return sessionKey, ae, nil 535 }