+2
-3
api/plc.go
+2
-3
api/plc.go
···
13
13
"net/url"
14
14
"strings"
15
15
16
-
key "github.com/bluesky-social/indigo/key"
17
16
did "github.com/whyrusleeping/go-did"
18
17
otel "go.opentelemetry.io/otel"
19
18
)
···
63
62
Sig string `json:"sig" cborgen:"sig,omitempty"`
64
63
}
65
64
66
-
func (s *PLCServer) CreateDID(ctx context.Context, sigkey *key.Key, recovery string, handle string, service string) (string, error) {
65
+
func (s *PLCServer) CreateDID(ctx context.Context, sigkey *did.PrivKey, recovery string, handle string, service string) (string, error) {
67
66
if s.C == nil {
68
67
s.C = http.DefaultClient
69
68
}
70
69
71
70
op := CreateOp{
72
71
Type: "create",
73
-
SigningKey: sigkey.DID(),
72
+
SigningKey: sigkey.Public().DID(),
74
73
RecoveryKey: recovery,
75
74
Handle: handle,
76
75
Service: service,
+4
-4
api/plc_test.go
+4
-4
api/plc_test.go
···
8
8
"fmt"
9
9
"testing"
10
10
11
-
key "github.com/bluesky-social/indigo/key"
12
11
"github.com/lestrrat-go/jwx/jwk"
12
+
did "github.com/whyrusleeping/go-did"
13
13
)
14
14
15
15
type testVector struct {
···
88
88
t.Fatal(err)
89
89
}
90
90
91
-
mk := key.Key{
91
+
mk := did.PrivKey{
92
92
Raw: &spk,
93
-
Type: "P-256",
93
+
Type: did.KeyTypeP256,
94
94
}
95
95
96
-
if mk.DID() != tv.Did {
96
+
if mk.Public().DID() != tv.Did {
97
97
t.Fatal("keys generated different DIDs")
98
98
}
99
99
+3
-2
bgs/bgs.go
+3
-2
bgs/bgs.go
···
175
175
176
176
u = new(User)
177
177
u.ID = subj.Uid
178
+
u.Did = evt.Repo
178
179
}
179
180
180
181
// TODO: if the user is already in the 'slow' path, we shouldnt even bother trying to fast path this event
181
182
182
-
if err := bgs.repoman.HandleExternalUserEvent(ctx, host.ID, u.ID, evt.RepoAppend.Prev, evt.RepoAppend.Ops, evt.RepoAppend.Car); err != nil {
183
+
if err := bgs.repoman.HandleExternalUserEvent(ctx, host.ID, u.ID, u.Did, evt.RepoAppend.Prev, evt.RepoAppend.Ops, evt.RepoAppend.Car); err != nil {
183
184
if !errors.Is(err, carstore.ErrRepoBaseMismatch) {
184
-
return err
185
+
return fmt.Errorf("handle user event failed: %w", err)
185
186
}
186
187
187
188
ai, err := bgs.Index.LookupUser(ctx, u.ID)
+12
-6
carstore/repo_test.go
+12
-6
carstore/repo_test.go
···
12
12
13
13
"github.com/bluesky-social/indigo/api"
14
14
"github.com/bluesky-social/indigo/repo"
15
+
"github.com/bluesky-social/indigo/util"
15
16
sqlbs "github.com/ipfs/go-bs-sqlite3"
16
17
"github.com/ipfs/go-cid"
17
18
flatfs "github.com/ipfs/go-ds-flatfs"
···
110
111
t.Fatal(err)
111
112
}
112
113
113
-
nroot, err := rr.Commit(ctx)
114
+
kmgr := &util.FakeKeyManager{}
115
+
nroot, err := rr.Commit(ctx, kmgr.SignForUser)
114
116
if err != nil {
115
117
t.Fatal(err)
116
118
}
···
132
134
}
133
135
134
136
func setupRepo(ctx context.Context, bs blockstore.Blockstore) (cid.Cid, error) {
135
-
nr := repo.NewRepo(ctx, bs)
137
+
nr := repo.NewRepo(ctx, "did:foo", bs)
136
138
137
139
if _, _, err := nr.CreateRecord(ctx, "app.bsky.feed.post", &api.PostRecord{
138
140
Text: fmt.Sprintf("hey look its a tweet %s", time.Now()),
···
140
142
return cid.Undef, err
141
143
}
142
144
143
-
ncid, err := nr.Commit(ctx)
145
+
kmgr := &util.FakeKeyManager{}
146
+
ncid, err := nr.Commit(ctx, kmgr.SignForUser)
144
147
if err != nil {
145
148
return cid.Undef, err
146
149
}
···
190
193
b.Fatal(err)
191
194
}
192
195
193
-
nroot, err := rr.Commit(ctx)
196
+
kmgr := &util.FakeKeyManager{}
197
+
nroot, err := rr.Commit(ctx, kmgr.SignForUser)
194
198
if err != nil {
195
199
b.Fatal(err)
196
200
}
···
232
236
b.Fatal(err)
233
237
}
234
238
235
-
nroot, err := rr.Commit(ctx)
239
+
kmgr := &util.FakeKeyManager{}
240
+
nroot, err := rr.Commit(ctx, kmgr.SignForUser)
236
241
if err != nil {
237
242
b.Fatal(err)
238
243
}
···
269
274
b.Fatal(err)
270
275
}
271
276
272
-
nroot, err := rr.Commit(ctx)
277
+
kmgr := &util.FakeKeyManager{}
278
+
nroot, err := rr.Commit(ctx, kmgr.SignForUser)
273
279
if err != nil {
274
280
b.Fatal(err)
275
281
}
+4
-3
cmd/bigsky/main.go
+4
-3
cmd/bigsky/main.go
···
114
114
return err
115
115
}
116
116
117
-
repoman := repomgr.NewRepoManager(db, cstore)
117
+
didr := &api.PLCServer{Host: cctx.String("plc")}
118
+
kmgr := indexer.NewKeyManager(didr, nil)
119
+
120
+
repoman := repomgr.NewRepoManager(db, cstore, kmgr)
118
121
119
122
evtman := events.NewEventManager()
120
123
···
123
126
// not necessary to generate notifications, should probably make the
124
127
// indexer just take optional callbacks for notification stuff
125
128
notifman := notifs.NewNotificationManager(db, repoman.GetRecord)
126
-
127
-
didr := &api.PLCServer{Host: cctx.String("plc")}
128
129
129
130
ix, err := indexer.NewIndexer(db, notifman, evtman, didr, repoman, true)
130
131
if err != nil {
+14
-5
cmd/gosky/main.go
+14
-5
cmd/gosky/main.go
···
15
15
atproto "github.com/bluesky-social/indigo/api/atproto"
16
16
bsky "github.com/bluesky-social/indigo/api/bsky"
17
17
cliutil "github.com/bluesky-social/indigo/cmd/gosky/util"
18
-
"github.com/bluesky-social/indigo/key"
19
18
"github.com/bluesky-social/indigo/repo"
20
19
"github.com/ipfs/go-cid"
21
20
"github.com/lestrrat-go/jwx/jwa"
···
24
23
rejson "github.com/polydawn/refmt/json"
25
24
"github.com/polydawn/refmt/shared"
26
25
cli "github.com/urfave/cli/v2"
26
+
"github.com/whyrusleeping/go-did"
27
27
)
28
28
29
29
func main() {
···
202
202
return err
203
203
}
204
204
205
-
fmt.Println("KEYDID: ", sigkey.DID())
205
+
fmt.Println("KEYDID: ", sigkey.Public().DID())
206
206
207
207
ndid, err := s.CreateDID(context.TODO(), sigkey, recoverydid, handle, service)
208
208
if err != nil {
···
214
214
},
215
215
}
216
216
217
-
func loadKey(kfile string) (*key.Key, error) {
217
+
func loadKey(kfile string) (*did.PrivKey, error) {
218
218
kb, err := os.ReadFile(kfile)
219
219
if err != nil {
220
220
return nil, err
···
234
234
return nil, fmt.Errorf("need a curve set")
235
235
}
236
236
237
-
return &key.Key{
237
+
var out string
238
+
kts := string(curve.(jwa.EllipticCurveAlgorithm))
239
+
switch kts {
240
+
case "P-256":
241
+
out = did.KeyTypeP256
242
+
default:
243
+
return nil, fmt.Errorf("unrecognized key type: %s", kts)
244
+
}
245
+
246
+
return &did.PrivKey{
238
247
Raw: &spk,
239
-
Type: string(curve.(jwa.EllipticCurveAlgorithm)),
248
+
Type: out,
240
249
}, nil
241
250
}
242
251
+1
-1
cmd/stress/main.go
+1
-1
cmd/stress/main.go
+2
-1
go.mod
+2
-1
go.mod
···
18
18
github.com/ipfs/go-log/v2 v2.5.1
19
19
github.com/ipld/go-car v0.5.0
20
20
github.com/ipld/go-car/v2 v2.5.1
21
+
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52
21
22
github.com/labstack/echo/v4 v4.10.0
22
23
github.com/lestrrat-go/jwx v1.2.25
23
24
github.com/lestrrat-go/jwx/v2 v2.0.8
···
29
30
github.com/stretchr/testify v1.8.1
30
31
github.com/urfave/cli/v2 v2.23.7
31
32
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa
32
-
github.com/whyrusleeping/go-did v0.0.0-20221105001742-8d9e0ffb0d59
33
+
github.com/whyrusleeping/go-did v0.0.0-20230210051655-85c9ba6709ab
33
34
go.opentelemetry.io/otel v1.11.2
34
35
go.opentelemetry.io/otel/exporters/jaeger v1.11.2
35
36
go.opentelemetry.io/otel/sdk v1.11.2
+6
-8
go.sum
+6
-8
go.sum
···
101
101
github.com/ipfs/go-car v0.0.4/go.mod h1:eZX0EppfsvSQN8IsJnx57bheogWMgQjJVWU/fDA7ySQ=
102
102
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
103
103
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
104
-
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
105
104
github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=
106
105
github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=
107
106
github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
···
144
143
github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8=
145
144
github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=
146
145
github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc=
147
-
github.com/ipfs/go-ipld-cbor v0.0.6 h1:pYuWHyvSpIsOOLw4Jy7NbBkCyzLDcl64Bf/LZW7eBQ0=
148
-
github.com/ipfs/go-ipld-cbor v0.0.6/go.mod h1:ssdxxaLJPXH7OjF5V4NSjBbcfh+evoR4ukuru0oPXMA=
149
146
github.com/ipfs/go-ipld-cbor v0.0.7-0.20230126201833-a73d038d90bc h1:eUEo764smNy0EVRuMTSmirmuh552Mf2aBjfpDcLnDa8=
150
147
github.com/ipfs/go-ipld-cbor v0.0.7-0.20230126201833-a73d038d90bc/go.mod h1:X7SgEIwC4COC5OWfcepZBWafO5kA1Rmt9ZsLLbhihQk=
151
148
github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms=
···
185
182
github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04=
186
183
github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4=
187
184
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73 h1:TsyATB2ZRRQGTwafJdgEUQkmjOExRV0DNokcihZxbnQ=
185
+
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 h1:QG4CGBqCeuBo6aZlGAamSkxWdgWfZGeE49eUOWJPA4c=
186
+
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52/go.mod h1:fdg+/X9Gg4AsAIzWpEHwnqd+QY3b7lajxyjE1m4hkq4=
188
187
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
189
188
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
190
189
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
···
442
441
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
443
442
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
444
443
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0=
445
-
github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI=
446
-
github.com/whyrusleeping/cbor-gen v0.0.0-20230109192608-0173f1e641ac h1:vSeRURgERu0v7h+bKvlP0wuT+inofyu61R15qka/Xh0=
447
-
github.com/whyrusleeping/cbor-gen v0.0.0-20230109192608-0173f1e641ac/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
448
444
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa h1:EyA027ZAkuaCLoxVX4r1TZMPy1d31fM6hbfQ4OU4I5o=
449
445
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ=
450
446
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
451
-
github.com/whyrusleeping/go-did v0.0.0-20221105001742-8d9e0ffb0d59 h1:dRYr/sfpZjX8evmbFrOG7ldkzdk5TLMGRVM40k1AZPQ=
452
-
github.com/whyrusleeping/go-did v0.0.0-20221105001742-8d9e0ffb0d59/go.mod h1:mX/AQ/SS9KrCwO8V+IWyIozytxw5gw75cMHymoJvMGo=
447
+
github.com/whyrusleeping/go-did v0.0.0-20230209234736-e14671c25e01 h1:YPGD8CeME7k4QgW0/lb1bR8nYSQDQbxoTPqb4zKke78=
448
+
github.com/whyrusleeping/go-did v0.0.0-20230209234736-e14671c25e01/go.mod h1:mX/AQ/SS9KrCwO8V+IWyIozytxw5gw75cMHymoJvMGo=
449
+
github.com/whyrusleeping/go-did v0.0.0-20230210051655-85c9ba6709ab h1:u1P8OjfkqR7LGZV91TMS76NEecUPa/boX29FBMNq+nw=
450
+
github.com/whyrusleeping/go-did v0.0.0-20230210051655-85c9ba6709ab/go.mod h1:qPtRyexGM5XMHFIfjH+EiA/A/1n2JakWEdMPC53pJAE=
453
451
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
454
452
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
455
453
github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8=
+62
indexer/keymgr.go
+62
indexer/keymgr.go
···
1
+
package indexer
2
+
3
+
import (
4
+
"context"
5
+
"crypto"
6
+
"fmt"
7
+
"time"
8
+
9
+
"github.com/bluesky-social/indigo/plc"
10
+
did "github.com/whyrusleeping/go-did"
11
+
)
12
+
13
+
type KeyManager struct {
14
+
didr plc.PLCClient
15
+
16
+
signingKey *did.PrivKey
17
+
}
18
+
19
+
func NewKeyManager(didr plc.PLCClient, k *did.PrivKey) *KeyManager {
20
+
return &KeyManager{
21
+
didr: didr,
22
+
signingKey: k,
23
+
}
24
+
}
25
+
26
+
type cachedKey struct {
27
+
cachedAt time.Time
28
+
pub crypto.PublicKey
29
+
}
30
+
31
+
func (km *KeyManager) VerifyUserSignature(ctx context.Context, did string, sig []byte, msg []byte) error {
32
+
k, err := km.getKey(ctx, did)
33
+
if err != nil {
34
+
return err
35
+
}
36
+
37
+
return k.Verify(msg, sig)
38
+
}
39
+
40
+
func (km *KeyManager) getKey(ctx context.Context, did string) (*did.PubKey, error) {
41
+
// TODO: caching should be done at the DID document level, that way we can
42
+
// have a thing that subscribes to plc updates for cache busting
43
+
doc, err := km.didr.GetDocument(ctx, did)
44
+
if err != nil {
45
+
return nil, err
46
+
}
47
+
48
+
pubk, err := doc.GetPublicKey("#signingKey")
49
+
if err != nil {
50
+
return nil, err
51
+
}
52
+
53
+
return pubk, nil
54
+
}
55
+
56
+
func (km *KeyManager) SignForUser(ctx context.Context, did string, msg []byte) ([]byte, error) {
57
+
if km.signingKey == nil {
58
+
return nil, fmt.Errorf("key manager does not have a signing key, cannot sign")
59
+
}
60
+
61
+
return km.signingKey.Sign(msg)
62
+
}
+1
-1
indexer/posts_test.go
+1
-1
indexer/posts_test.go
-87
key/key.go
-87
key/key.go
···
1
-
package key
2
-
3
-
import (
4
-
"crypto/ecdsa"
5
-
"crypto/ed25519"
6
-
"crypto/elliptic"
7
-
"crypto/rand"
8
-
"crypto/sha256"
9
-
"crypto/x509"
10
-
"fmt"
11
-
12
-
"github.com/multiformats/go-multibase"
13
-
"github.com/multiformats/go-varint"
14
-
)
15
-
16
-
const (
17
-
MCed25519 = 0xED
18
-
MCP256 = 0x1200
19
-
)
20
-
21
-
type Key struct {
22
-
Raw interface{}
23
-
Type string
24
-
}
25
-
26
-
func (k *Key) Sign(b []byte) ([]byte, error) {
27
-
switch k.Type {
28
-
case "ed25519":
29
-
return ed25519.Sign(k.Raw.(ed25519.PrivateKey), b), nil
30
-
case "P-256":
31
-
h := sha256.Sum256(b)
32
-
//return ecdsa.SignASN1(rand.Reader, k.Raw.(*ecdsa.PrivateKey), h[:])
33
-
r, s, err := ecdsa.Sign(rand.Reader, k.Raw.(*ecdsa.PrivateKey), h[:])
34
-
if err != nil {
35
-
return nil, err
36
-
}
37
-
38
-
return append(r.Bytes(), s.Bytes()...), nil
39
-
default:
40
-
return nil, fmt.Errorf("unsupported key type: %s", k.Type)
41
-
}
42
-
}
43
-
44
-
func (k *Key) DID() string {
45
-
var buf []byte
46
-
switch k.Type {
47
-
case "ed25519":
48
-
kb := k.Raw.(ed25519.PrivateKey)
49
-
buf := make([]byte, 8+len(kb))
50
-
n := varint.PutUvarint(buf, MCed25519)
51
-
copy(buf[n:], kb)
52
-
buf = buf[:n+len(kb)]
53
-
case "P-256":
54
-
sk := k.Raw.(*ecdsa.PrivateKey)
55
-
enc := elliptic.MarshalCompressed(elliptic.P256(), sk.X, sk.Y)
56
-
57
-
buf = make([]byte, 8+len(enc))
58
-
n := varint.PutUvarint(buf, MCP256)
59
-
copy(buf[n:], enc)
60
-
buf = buf[:n+len(enc)]
61
-
default:
62
-
return "<invalid key type>"
63
-
}
64
-
65
-
kstr, err := multibase.Encode(multibase.Base58BTC, buf)
66
-
if err != nil {
67
-
panic(err)
68
-
}
69
-
70
-
return "did:key:" + kstr
71
-
}
72
-
73
-
func (k *Key) RawBytes() ([]byte, error) {
74
-
switch k.Type {
75
-
case "ed25519":
76
-
return k.Raw.([]byte), nil
77
-
case "P-256":
78
-
b, err := x509.MarshalECPrivateKey(k.Raw.(*ecdsa.PrivateKey))
79
-
if err != nil {
80
-
return nil, err
81
-
}
82
-
83
-
return b, nil
84
-
default:
85
-
return nil, fmt.Errorf("unsupported key type: %q", k.Type)
86
-
}
87
-
}
+1
-1
pds/auth.go
+1
-1
pds/auth.go
+4
-4
pds/fedmgr.go
+4
-4
pds/fedmgr.go
···
10
10
"time"
11
11
12
12
"github.com/bluesky-social/indigo/events"
13
-
"github.com/bluesky-social/indigo/key"
14
13
15
14
"github.com/gorilla/websocket"
16
15
cbg "github.com/whyrusleeping/cbor-gen"
16
+
"github.com/whyrusleeping/go-did"
17
17
"gorm.io/gorm"
18
18
)
19
19
···
27
27
cb IndexCallback
28
28
29
29
db *gorm.DB
30
-
signingKey *key.Key
30
+
signingKey *did.PrivKey
31
31
}
32
32
33
-
func NewSlurper(cb IndexCallback, db *gorm.DB, signingKey *key.Key) Slurper {
33
+
func NewSlurper(cb IndexCallback, db *gorm.DB, signingKey *did.PrivKey) Slurper {
34
34
return Slurper{
35
35
cb: cb,
36
36
db: db,
···
55
55
var backoff int
56
56
for {
57
57
h := http.Header{
58
-
"DID": []string{s.signingKey.DID()},
58
+
"DID": []string{s.signingKey.Public().DID()},
59
59
}
60
60
61
61
con, res, err := d.Dial("ws://"+host.Host+"/events", h)
+2
-2
pds/handlers.go
+2
-2
pds/handlers.go
···
394
394
}
395
395
396
396
if recoveryKey == "" {
397
-
recoveryKey = s.signingKey.DID()
397
+
recoveryKey = s.signingKey.Public().DID()
398
398
}
399
399
400
400
d, err := s.plc.CreateDID(ctx, s.signingKey, recoveryKey, input.Handle, s.serviceUrl)
···
461
461
462
462
func (s *Server) handleComAtprotoHandleResolve(ctx context.Context, handle string) (*comatprototypes.HandleResolve_Output, error) {
463
463
if handle == "" {
464
-
return &comatprototypes.HandleResolve_Output{Did: s.signingKey.DID()}, nil
464
+
return &comatprototypes.HandleResolve_Output{Did: s.signingKey.Public().DID()}, nil
465
465
}
466
466
u, err := s.lookupUserByHandle(ctx, handle)
467
467
if err != nil {
+18
-7
pds/server.go
+18
-7
pds/server.go
···
17
17
"github.com/bluesky-social/indigo/carstore"
18
18
"github.com/bluesky-social/indigo/events"
19
19
"github.com/bluesky-social/indigo/indexer"
20
-
"github.com/bluesky-social/indigo/key"
21
20
"github.com/bluesky-social/indigo/lex/util"
22
21
"github.com/bluesky-social/indigo/models"
23
22
"github.com/bluesky-social/indigo/notifs"
···
33
32
"github.com/lestrrat-go/jwx/jwa"
34
33
jwk "github.com/lestrrat-go/jwx/jwk"
35
34
jwt "github.com/lestrrat-go/jwx/jwt"
35
+
"github.com/whyrusleeping/go-did"
36
36
"gorm.io/gorm"
37
37
)
38
38
···
47
47
indexer *indexer.Indexer
48
48
events *events.EventManager
49
49
slurper *Slurper
50
-
signingKey *key.Key
50
+
signingKey *did.PrivKey
51
51
echo *echo.Echo
52
52
jwtSigningKey []byte
53
53
enforcePeering bool
···
72
72
73
73
evtman := events.NewEventManager()
74
74
75
-
repoman := repomgr.NewRepoManager(db, cs)
75
+
kmgr := indexer.NewKeyManager(didr, serkey)
76
+
77
+
repoman := repomgr.NewRepoManager(db, cs, kmgr)
76
78
notifman := notifs.NewNotificationManager(db, repoman.GetRecord)
77
79
78
80
ix, err := indexer.NewIndexer(db, notifman, evtman, didr, repoman, false)
···
145
147
u.ID = subj.Uid
146
148
}
147
149
148
-
return s.repoman.HandleExternalUserEvent(ctx, host.ID, u.ID, evt.RepoAppend.Prev, evt.RepoAppend.Ops, evt.RepoAppend.Car)
150
+
return s.repoman.HandleExternalUserEvent(ctx, host.ID, u.ID, u.Did, evt.RepoAppend.Prev, evt.RepoAppend.Ops, evt.RepoAppend.Car)
149
151
default:
150
152
return fmt.Errorf("invalid fed event")
151
153
}
···
292
294
return util.CborDecodeValue(blk.RawData())
293
295
}
294
296
295
-
func loadKey(kfile string) (*key.Key, error) {
297
+
func loadKey(kfile string) (*did.PrivKey, error) {
296
298
kb, err := os.ReadFile(kfile)
297
299
if err != nil {
298
300
return nil, err
···
312
314
return nil, fmt.Errorf("need a curve set")
313
315
}
314
316
315
-
return &key.Key{
317
+
var out string
318
+
kts := string(curve.(jwa.EllipticCurveAlgorithm))
319
+
switch kts {
320
+
case "P-256":
321
+
out = did.KeyTypeP256
322
+
default:
323
+
return nil, fmt.Errorf("unrecognized key type: %s", kts)
324
+
}
325
+
326
+
return &did.PrivKey{
316
327
Raw: &spk,
317
-
Type: string(curve.(jwa.EllipticCurveAlgorithm)),
328
+
Type: out,
318
329
}, nil
319
330
}
320
331
+19
-9
plc/fakedid.go
+19
-9
plc/fakedid.go
···
5
5
"crypto/rand"
6
6
"encoding/hex"
7
7
8
-
"github.com/bluesky-social/indigo/key"
9
8
"github.com/whyrusleeping/go-did"
10
9
"gorm.io/gorm"
11
10
)
12
11
13
12
type FakeDidMapping struct {
14
13
gorm.Model
15
-
Handle string
16
-
Did string `gorm:"index"`
17
-
Service string
14
+
Handle string
15
+
Did string `gorm:"index"`
16
+
Service string
17
+
KeyType string
18
+
PubKeyMbase string
18
19
}
19
20
20
21
type FakeDid struct {
···
46
47
47
48
//Authentication []interface{} `json:"authentication"`
48
49
49
-
//VerificationMethod []VerificationMethod `json:"verificationMethod"`
50
+
VerificationMethod: []did.VerificationMethod{
51
+
did.VerificationMethod{
52
+
ID: "#signingKey",
53
+
Type: rec.KeyType,
54
+
PublicKeyMultibase: &rec.PubKeyMbase,
55
+
Controller: rec.Did,
56
+
},
57
+
},
50
58
51
59
Service: []did.Service{
52
60
did.Service{
···
58
66
}, nil
59
67
}
60
68
61
-
func (fd *FakeDid) CreateDID(ctx context.Context, sigkey *key.Key, recovery string, handle string, service string) (string, error) {
69
+
func (fd *FakeDid) CreateDID(ctx context.Context, sigkey *did.PrivKey, recovery string, handle string, service string) (string, error) {
62
70
buf := make([]byte, 8)
63
71
rand.Read(buf)
64
72
d := "did:plc:" + hex.EncodeToString(buf)
65
73
66
74
if err := fd.db.Create(&FakeDidMapping{
67
-
Handle: handle,
68
-
Did: d,
69
-
Service: service,
75
+
Handle: handle,
76
+
Did: d,
77
+
Service: service,
78
+
PubKeyMbase: sigkey.Public().MultibaseString(),
79
+
KeyType: sigkey.KeyType(),
70
80
}).Error; err != nil {
71
81
return "", err
72
82
}
+1
-2
plc/plc.go
+1
-2
plc/plc.go
···
3
3
import (
4
4
"context"
5
5
6
-
"github.com/bluesky-social/indigo/key"
7
6
"github.com/whyrusleeping/go-did"
8
7
)
9
8
10
9
type PLCClient interface {
11
10
GetDocument(ctx context.Context, didstr string) (*did.Document, error)
12
-
CreateDID(ctx context.Context, sigkey *key.Key, recovery string, handle string, service string) (string, error)
11
+
CreateDID(ctx context.Context, sigkey *did.PrivKey, recovery string, handle string, service string) (string, error)
13
12
}
+50
-3
repo/repo.go
+50
-3
repo/repo.go
···
85
85
return OpenRepo(ctx, bs, root)
86
86
}
87
87
88
-
func NewRepo(ctx context.Context, bs blockstore.Blockstore) *Repo {
88
+
func NewRepo(ctx context.Context, did string, bs blockstore.Blockstore) *Repo {
89
89
cst := util.CborStore(bs)
90
90
91
91
t := mst.NewMST(cst, 32, cid.Undef, []mst.NodeEntry{}, 0)
92
92
93
+
meta := Meta{
94
+
Datastore: "TODO",
95
+
Did: did,
96
+
Version: 1,
97
+
}
98
+
93
99
return &Repo{
100
+
meta: meta,
94
101
cst: cst,
95
102
bs: bs,
96
103
mst: t,
···
106
113
return nil, fmt.Errorf("loading root from blockstore: %w", err)
107
114
}
108
115
116
+
var rt Root
117
+
if err := cst.Get(ctx, sr.Root, &rt); err != nil {
118
+
return nil, fmt.Errorf("loading root: %w", err)
119
+
}
120
+
121
+
var meta Meta
122
+
if err := cst.Get(ctx, rt.Meta, &meta); err != nil {
123
+
return nil, fmt.Errorf("loading meta: %w", err)
124
+
}
125
+
109
126
return &Repo{
110
127
sr: sr,
111
128
bs: bs,
112
129
cst: cst,
113
130
repoCid: root,
131
+
meta: meta,
114
132
}, nil
115
133
}
116
134
···
118
136
MarshalCBOR(w io.Writer) error
119
137
}
120
138
139
+
func (r *Repo) MetaCid(ctx context.Context) (cid.Cid, error) {
140
+
var root Root
141
+
if err := r.cst.Get(ctx, r.sr.Root, &root); err != nil {
142
+
return cid.Undef, err
143
+
}
144
+
145
+
return root.Meta, nil
146
+
}
147
+
148
+
func (r *Repo) RepoDid() string {
149
+
if r.meta.Did == "" {
150
+
panic("repo has unset did")
151
+
}
152
+
153
+
return r.meta.Did
154
+
}
155
+
121
156
func (r *Repo) PrevCommit(ctx context.Context) (*cid.Cid, error) {
122
-
123
157
var c Root
124
158
if err := r.cst.Get(ctx, r.sr.Root, &c); err != nil {
125
159
return nil, fmt.Errorf("loading previous commit: %w", err)
···
130
164
131
165
func (r *Repo) CommitRoot() cid.Cid {
132
166
return r.sr.Root
167
+
}
168
+
169
+
func (r *Repo) SignedCommit() SignedCommit {
170
+
return r.sr
133
171
}
134
172
135
173
func (r *Repo) Blockstore() blockstore.Blockstore {
···
205
243
return nil
206
244
}
207
245
208
-
func (r *Repo) Commit(ctx context.Context) (cid.Cid, error) {
246
+
func (r *Repo) Commit(ctx context.Context, signer func(context.Context, string, []byte) ([]byte, error)) (cid.Cid, error) {
209
247
ctx, span := otel.Tracer("repo").Start(ctx, "Commit")
210
248
defer span.End()
211
249
···
235
273
if err != nil {
236
274
return cid.Undef, err
237
275
}
276
+
fmt.Println("PUT NEW META: ", mcid)
238
277
nroot.Meta = mcid
239
278
}
240
279
···
243
282
return cid.Undef, err
244
283
}
245
284
285
+
did := r.RepoDid()
286
+
287
+
sig, err := signer(ctx, did, ncomcid.Bytes())
288
+
if err != nil {
289
+
return cid.Undef, fmt.Errorf("failed to sign root: %w", err)
290
+
}
291
+
246
292
nsroot := SignedCommit{
247
293
Root: ncomcid,
294
+
Sig: sig,
248
295
}
249
296
250
297
nsrootcid, err := r.cst.Put(ctx, &nsroot)
+2
-1
repomgr/ingest_test.go
+2
-1
repomgr/ingest_test.go
···
8
8
"testing"
9
9
10
10
"github.com/bluesky-social/indigo/carstore"
11
+
"github.com/bluesky-social/indigo/util"
11
12
"github.com/ipfs/go-cid"
12
13
"gorm.io/driver/sqlite"
13
14
"gorm.io/gorm"
···
53
54
t.Fatal(err)
54
55
}
55
56
56
-
repoman := NewRepoManager(maindb, cs)
57
+
repoman := NewRepoManager(maindb, cs, &util.FakeKeyManager{})
57
58
58
59
fi, err := os.Open("testrepo.car")
59
60
if err != nil {
+57
-11
repomgr/repomgr.go
+57
-11
repomgr/repomgr.go
···
30
30
31
31
var log = logging.Logger("repomgr")
32
32
33
-
func NewRepoManager(db *gorm.DB, cs *carstore.CarStore) *RepoManager {
33
+
func NewRepoManager(db *gorm.DB, cs *carstore.CarStore, kmgr KeyManager) *RepoManager {
34
34
db.AutoMigrate(RepoHead{})
35
35
36
36
return &RepoManager{
37
37
db: db,
38
38
cs: cs,
39
39
userLocks: make(map[uint]*userLock),
40
+
kmgr: kmgr,
40
41
}
41
42
}
42
43
44
+
type KeyManager interface {
45
+
VerifyUserSignature(context.Context, string, []byte, []byte) error
46
+
SignForUser(context.Context, string, []byte) ([]byte, error)
47
+
}
48
+
43
49
func (rm *RepoManager) SetEventHandler(cb func(context.Context, *RepoEvent)) {
44
50
rm.events = cb
45
51
}
46
52
47
53
type RepoManager struct {
48
-
cs *carstore.CarStore
49
-
db *gorm.DB
54
+
cs *carstore.CarStore
55
+
db *gorm.DB
56
+
kmgr KeyManager
50
57
51
58
lklk sync.Mutex
52
59
userLocks map[uint]*userLock
···
193
200
return "", cid.Undef, err
194
201
}
195
202
196
-
nroot, err := r.Commit(ctx)
203
+
nroot, err := r.Commit(ctx, rm.kmgr.SignForUser)
197
204
if err != nil {
198
205
return "", cid.Undef, err
199
206
}
···
255
262
return cid.Undef, err
256
263
}
257
264
258
-
nroot, err := r.Commit(ctx)
265
+
nroot, err := r.Commit(ctx, rm.kmgr.SignForUser)
259
266
if err != nil {
260
267
return cid.Undef, err
261
268
}
···
316
323
return err
317
324
}
318
325
319
-
nroot, err := r.Commit(ctx)
326
+
nroot, err := r.Commit(ctx, rm.kmgr.SignForUser)
320
327
if err != nil {
321
328
return err
322
329
}
···
366
373
return err
367
374
}
368
375
369
-
r := repo.NewRepo(ctx, ds)
376
+
r := repo.NewRepo(ctx, did, ds)
370
377
371
378
profile := &bsky.ActorProfile{
372
379
DisplayName: displayname,
···
391
398
392
399
// TODO: set declaration?
393
400
394
-
root, err := r.Commit(ctx)
401
+
root, err := r.Commit(ctx, rm.kmgr.SignForUser)
395
402
if err != nil {
396
-
return err
403
+
return fmt.Errorf("committing repo for actor init: %w", err)
397
404
}
398
405
399
406
rslice, err := ds.CloseWithRoot(ctx, root)
···
497
504
return ap, nil
498
505
}
499
506
500
-
func (rm *RepoManager) HandleExternalUserEvent(ctx context.Context, pdsid uint, uid uint, prev *cid.Cid, ops []*events.RepoOp, carslice []byte) error {
507
+
func (rm *RepoManager) HandleExternalUserEvent(ctx context.Context, pdsid uint, uid uint, did string, prev *cid.Cid, ops []*events.RepoOp, carslice []byte) error {
501
508
ctx, span := otel.Tracer("repoman").Start(ctx, "HandleExternalUserEvent")
502
509
defer span.End()
503
510
···
516
523
return fmt.Errorf("opening external user repo: %w", err)
517
524
}
518
525
526
+
repoDid := r.RepoDid()
527
+
528
+
if did != repoDid {
529
+
return fmt.Errorf("DID in repo did not match (%q != %q)", did, repoDid)
530
+
}
531
+
532
+
scom := r.SignedCommit()
533
+
534
+
if err := rm.kmgr.VerifyUserSignature(ctx, repoDid, scom.Sig, scom.Root.Bytes()); err != nil {
535
+
return fmt.Errorf("signature check failed: %w", err)
536
+
}
537
+
519
538
log.Infow("external event", "uid", uid, "ops", ops)
520
539
521
540
var evtops []RepoOp
···
675
694
}
676
695
}
677
696
678
-
nroot, err := r.Commit(ctx)
697
+
nroot, err := r.Commit(ctx, rm.kmgr.SignForUser)
679
698
if err != nil {
680
699
return err
681
700
}
···
812
831
}
813
832
814
833
membs := blockstore.NewBlockstore(datastore.NewMapDatastore())
834
+
835
+
// mild hack: without access to the 'meta' object, we cant properly verify each new repo slice has the right DID in the case of a gap fill procedure
836
+
if until.Defined() {
837
+
robs, err := rm.cs.ReadOnlySession(user)
838
+
if err != nil {
839
+
return err
840
+
}
841
+
842
+
old, err := repo.OpenRepo(ctx, robs, until)
843
+
if err != nil {
844
+
return err
845
+
}
846
+
847
+
mcid, err := old.MetaCid(ctx)
848
+
if err != nil {
849
+
return err
850
+
}
851
+
852
+
blk, err := robs.Get(ctx, mcid)
853
+
if err != nil {
854
+
return err
855
+
}
856
+
857
+
if err := membs.Put(ctx, blk); err != nil {
858
+
return err
859
+
}
860
+
}
815
861
816
862
for {
817
863
blk, err := carr.Next()
+6
-2
testing/utils.go
+6
-2
testing/utils.go
···
399
399
return nil, err
400
400
}
401
401
402
-
repoman := repomgr.NewRepoManager(maindb, cs)
402
+
kmgr := indexer.NewKeyManager(didr, nil)
403
+
404
+
repoman := repomgr.NewRepoManager(maindb, cs, kmgr)
403
405
404
406
notifman := notifs.NewNotificationManager(maindb, repoman.GetRecord)
405
407
···
761
763
}
762
764
}
763
765
764
-
nroot, err := r.Commit(ctx)
766
+
kmgr := &bsutil.FakeKeyManager{}
767
+
768
+
nroot, err := r.Commit(ctx, kmgr.SignForUser)
765
769
if err != nil {
766
770
return cid.Undef, err
767
771
}
+14
util/fakekey.go
+14
util/fakekey.go
···
1
+
package util
2
+
3
+
import "context"
4
+
5
+
type FakeKeyManager struct {
6
+
}
7
+
8
+
func (km *FakeKeyManager) VerifyUserSignature(context.Context, string, []byte, []byte) error {
9
+
return nil
10
+
}
11
+
12
+
func (km *FakeKeyManager) SignForUser(ctx context.Context, did string, msg []byte) ([]byte, error) {
13
+
return []byte("signature"), nil
14
+
}