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