github.com/metacurrency/holochain@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/agent.go (about) 1 // Copyright (C) 2013-2018, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.) 2 // Use of this source code is governed by GPLv3 found in the LICENSE file 3 //--------------------------------------------------------------------------------------- 4 // key generation and marshal/unmarshaling and abstraction of agent for holochains 5 6 package holochain 7 8 import ( 9 "crypto/rand" 10 "errors" 11 "fmt" 12 b58 "github.com/jbenet/go-base58" 13 ic "github.com/libp2p/go-libp2p-crypto" 14 peer "github.com/libp2p/go-libp2p-peer" 15 "io" 16 "os" 17 "path/filepath" 18 "runtime" 19 ) 20 21 // AgentIdentity is the user's unique identity information in context of this holochain. 22 // it follows AgentIdentitySchema in DNA 23 type AgentIdentity string 24 25 type AgentType int 26 27 const ( 28 LibP2P = iota 29 ) 30 31 // Agent abstracts the key behaviors and connection to a holochain node address 32 // Note that this is currently only a partial abstraction because the NodeID is always a libp2p peer.ID 33 // to complete the abstraction so we could use other libraries for p2p2 network transaction we 34 // would need to also abstract a matching NodeID type 35 type Agent interface { 36 Identity() AgentIdentity 37 SetIdentity(id AgentIdentity) 38 AgentType() AgentType 39 GenKeys(seed io.Reader) error 40 PrivKey() ic.PrivKey 41 PubKey() ic.PubKey 42 EncodePubKey() (string, error) 43 NodeID() (peer.ID, string, error) 44 AgentEntry(revocation Revocation) (AgentEntry, error) 45 } 46 47 type LibP2PAgent struct { 48 identity AgentIdentity 49 priv ic.PrivKey 50 pub ic.PubKey // cached so as not to recalculate all the time 51 } 52 53 func (a *LibP2PAgent) Identity() AgentIdentity { 54 return a.identity 55 } 56 func (a *LibP2PAgent) SetIdentity(id AgentIdentity) { 57 a.identity = id 58 } 59 60 func (a *LibP2PAgent) AgentType() AgentType { 61 return LibP2P 62 } 63 64 func (a *LibP2PAgent) PrivKey() ic.PrivKey { 65 return a.priv 66 } 67 68 func (a *LibP2PAgent) PubKey() ic.PubKey { 69 return a.pub 70 } 71 72 func (a *LibP2PAgent) EncodePubKey() (b58pk string, err error) { 73 var pk []byte 74 pk, err = ic.MarshalPublicKey(a.pub) 75 if err != nil { 76 return 77 } 78 b58pk = b58.Encode(pk) 79 return 80 } 81 82 func DecodePubKey(b58pk string) (pubKey ic.PubKey, err error) { 83 var pubKeyBytes []byte 84 pubKeyBytes = b58.Decode(b58pk) 85 pubKey, err = ic.UnmarshalPublicKey(pubKeyBytes) 86 return 87 } 88 89 func (a *LibP2PAgent) GenKeys(seed io.Reader) (err error) { 90 var priv ic.PrivKey 91 if seed == nil { 92 seed = rand.Reader 93 } 94 priv, _, err = ic.GenerateEd25519Key(seed) 95 if err != nil { 96 return 97 } 98 a.priv = priv 99 a.pub = priv.GetPublic() 100 return 101 } 102 103 func (a *LibP2PAgent) NodeID() (nodeID peer.ID, nodeIDStr string, err error) { 104 nodeID, err = peer.IDFromPrivateKey(a.PrivKey()) 105 if err == nil { 106 nodeIDStr = peer.IDB58Encode(nodeID) 107 } 108 return 109 } 110 111 func (a *LibP2PAgent) AgentEntry(revocation Revocation) (entry AgentEntry, err error) { 112 113 entry = AgentEntry{ 114 Identity: a.Identity(), 115 } 116 if revocation != nil { 117 entry.Revocation, err = revocation.Marshal() 118 if err != nil { 119 return 120 } 121 } 122 entry.PublicKey, err = a.EncodePubKey() 123 if err != nil { 124 return 125 } 126 return 127 } 128 129 // NewAgent creates an agent structure of the given type 130 // Note: currently only IPFS agents are implemented 131 func NewAgent(agentType AgentType, identity AgentIdentity, seed io.Reader) (agent Agent, err error) { 132 switch agentType { 133 case LibP2P: 134 a := LibP2PAgent{ 135 identity: identity, 136 } 137 err = a.GenKeys(seed) 138 if err != nil { 139 return 140 } 141 agent = &a 142 default: 143 err = fmt.Errorf("unknown key type: %d", agentType) 144 } 145 return 146 } 147 148 // SaveAgent saves out the keys and agent name to the given directory 149 func SaveAgent(path string, agent Agent) (err error) { 150 WriteFile([]byte(agent.Identity()), path, AgentFileName) 151 if err != nil { 152 return 153 } 154 if FileExists(path, PrivKeyFileName) { 155 return errors.New("keys already exist") 156 } 157 var k []byte 158 k, err = agent.PrivKey().Bytes() 159 if err != nil { 160 return 161 } 162 err = WriteFile(k, path, PrivKeyFileName) 163 os.Chmod(filepath.Join(path, PrivKeyFileName), OS_USER_R) 164 return 165 } 166 167 // LoadAgent gets the agent identity and private key from the specified directory 168 // TODO confirm against chain? 169 func LoadAgent(path string) (agent Agent, err error) { 170 var perms os.FileMode 171 172 // TODO, make this check also work on windows instead of just bypassing! 173 if runtime.GOOS != "windows" { 174 perms, err = filePerms(path, PrivKeyFileName) 175 if err != nil { 176 return 177 } 178 if perms != OS_USER_R { 179 err = errors.New(filepath.Join(path, PrivKeyFileName) + " file not read-only") 180 return 181 } 182 } 183 var identity []byte 184 identity, err = ReadFile(path, AgentFileName) 185 if err != nil { 186 return 187 } 188 a := LibP2PAgent{ 189 identity: AgentIdentity(identity), 190 } 191 k, err := ReadFile(path, PrivKeyFileName) 192 if err != nil { 193 return nil, err 194 } 195 a.priv, err = ic.UnmarshalPrivateKey(k) 196 if err != nil { 197 return 198 } 199 a.pub = a.priv.GetPublic() 200 agent = &a 201 return 202 }