github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/cmd/did/diddoc.go (about)

     1  // Copyright (c) 2020 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package did
     7  
     8  import (
     9  	"crypto/sha256"
    10  	"encoding/json"
    11  
    12  	"github.com/btcsuite/btcutil/base58"
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/ethereum/go-ethereum/crypto"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  const (
    19  	// DIDPrefix is the prefix string
    20  	DIDPrefix = "did:io:"
    21  	// DIDAuthType is the authentication type
    22  	DIDAuthType = "EcdsaSecp256k1VerificationKey2019"
    23  	// DIDOwner is the suffix string
    24  	DIDOwner = "#owner"
    25  
    26  	// KnownDIDContext known context for did
    27  	KnownDIDContext = "https://www.w3.org/ns/did/v1"
    28  	// Secp256k1DIDContext secp256k1 context for did
    29  	Secp256k1DIDContext = "https://w3id.org/security/suites/secp256k1-2019/v1"
    30  )
    31  
    32  type (
    33  	verificationMethod struct {
    34  		ID              string `json:"id"`
    35  		Type            string `json:"type"`
    36  		Controller      string `json:"controller"`
    37  		PublicKeyBase58 string `json:"publicKeyBase58,omitempty"`
    38  	}
    39  
    40  	verificationMethodSet interface{}
    41  
    42  	serviceStruct struct {
    43  		ID              string `json:"id,omitempty"`
    44  		Type            string `json:"type,omitempty"`
    45  		ServiceEndpoint string `json:"serviceEndpoint,omitempty"`
    46  	}
    47  
    48  	// Doc is the DID document struct
    49  	Doc struct {
    50  		Context            interface{}             `json:"@context,omitempty"`
    51  		ID                 string                  `json:"id,omitempty"`
    52  		Controller         string                  `json:"controller,omitempty"`
    53  		VerificationMethod []verificationMethod    `json:"verificationMethod,omitempty"`
    54  		Authentication     []verificationMethodSet `json:"authentication,omitempty"`
    55  		AssertionMethod    []verificationMethodSet `json:"assertionMethod,omitempty"`
    56  		Service            []serviceStruct         `json:"service,omitempty"`
    57  	}
    58  )
    59  
    60  // Owner did document owner
    61  func (doc *Doc) Owner() common.Address {
    62  	return common.HexToAddress(doc.ID[7:])
    63  }
    64  
    65  // Bytes did document bytes
    66  func (doc *Doc) Bytes() ([]byte, error) {
    67  	return json.MarshalIndent(doc, "", "  ")
    68  }
    69  
    70  // JSON did document json
    71  func (doc *Doc) JSON() (string, error) {
    72  	data, err := doc.Bytes()
    73  	if err != nil {
    74  		return "", err
    75  	}
    76  	return string(data), nil
    77  }
    78  
    79  // Hash did document hash
    80  func (doc *Doc) Hash() ([32]byte, error) {
    81  	data, err := doc.Bytes()
    82  	if err != nil {
    83  		return [32]byte{}, err
    84  	}
    85  	return sha256.Sum256(data), nil
    86  }
    87  
    88  // AddService add service to did document
    89  func (doc *Doc) AddService(tag, serviceType, endpoint string) {
    90  	id := doc.ID + "#" + tag
    91  	if doc.Service == nil {
    92  		doc.Service = []serviceStruct{{
    93  			ID:              id,
    94  			Type:            serviceType,
    95  			ServiceEndpoint: endpoint,
    96  		}}
    97  		return
    98  	}
    99  	for _, service := range doc.Service {
   100  		if service.ID == id {
   101  			service.Type = serviceType
   102  			service.ServiceEndpoint = endpoint
   103  			return
   104  		}
   105  	}
   106  	doc.Service = append(doc.Service, serviceStruct{
   107  		ID:              id,
   108  		Type:            serviceType,
   109  		ServiceEndpoint: endpoint,
   110  	})
   111  }
   112  
   113  // RemoveService remove service from did document
   114  func (doc *Doc) RemoveService(tag string) error {
   115  	id := doc.ID + "#" + tag
   116  	if doc.Service == nil || len(doc.Service) == 0 {
   117  		return errors.New("service not exists")
   118  	}
   119  	services := make([]serviceStruct, len(doc.Service)-1)
   120  	count := 0
   121  	for i, service := range doc.Service {
   122  		if service.ID != id {
   123  			if count == len(services) {
   124  				return errors.New("service not exists")
   125  			}
   126  			services[count] = doc.Service[i]
   127  			count++
   128  		}
   129  	}
   130  	doc.Service = services
   131  	return nil
   132  }
   133  
   134  // NewDIDDoc new did document by public key
   135  func NewDIDDoc(publicKey []byte) (*Doc, error) {
   136  	pubKey, err := crypto.UnmarshalPubkey(publicKey)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	compressedPubKey := crypto.CompressPubkey(pubKey)
   141  	address := crypto.PubkeyToAddress(*pubKey)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	doc := &Doc{
   147  		Context: []string{
   148  			KnownDIDContext,
   149  			Secp256k1DIDContext,
   150  		},
   151  	}
   152  	doc.ID = DIDPrefix + address.Hex()
   153  	key0 := doc.ID + "#key-0"
   154  	doc.VerificationMethod = []verificationMethod{{
   155  		ID:              key0,
   156  		Type:            DIDAuthType,
   157  		Controller:      doc.ID,
   158  		PublicKeyBase58: base58.Encode(compressedPubKey),
   159  	}}
   160  	doc.Authentication = []verificationMethodSet{key0}
   161  	doc.AssertionMethod = []verificationMethodSet{key0}
   162  	return doc, nil
   163  }