From cea8a616f826a6065055ad30a5f0cb78685411e7 Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Wed, 18 Feb 2026 19:40:20 -0800 Subject: [PATCH 1/4] cliutil: rewrite private key JWK helpers --- util/cliutil/key.go | 31 +++++++++++++++++-------------- util/cliutil/key_test.go | 8 +------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/util/cliutil/key.go b/util/cliutil/key.go index 2a513274..7ed1b029 100644 --- a/util/cliutil/key.go +++ b/util/cliutil/key.go @@ -9,14 +9,17 @@ import ( "os" "path/filepath" + "github.com/bluesky-social/indigo/atproto/atcrypto" + "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" - "github.com/whyrusleeping/go-did" ) -// LoadKeyFromFile reads the private key from file -func LoadKeyFromFile(kfile string) (*did.PrivKey, error) { - kb, err := os.ReadFile(kfile) +// Loads a secret key from JWK JSON on disk. Only supports P-256 format. +// +// Deprecated: use the indigo atcrypto package and multikey string serialization instead +func LoadKeyFromFile(fpath string) (*atcrypto.PrivateKeyP256, error) { + kb, err := os.ReadFile(fpath) if err != nil { return nil, err } @@ -35,22 +38,22 @@ func LoadKeyFromFile(kfile string) (*did.PrivKey, error) { return nil, fmt.Errorf("need a curve set") } - var out string kts := string(curve.(jwa.EllipticCurveAlgorithm)) - switch kts { - case "P-256": - out = did.KeyTypeP256 - default: + if kts != "P-256" { return nil, fmt.Errorf("unrecognized key type: %s", kts) } - return &did.PrivKey{ - Raw: &spk, - Type: out, - }, nil + skECDH, err := spk.ECDH() + if err != nil { + return nil, err + } + + return atcrypto.ParsePrivateBytesP256(skECDH.Bytes()) } -// GenerateKeyToFile makes the private key and store it into the file +// Generates a P-256 secret key and saves it to disk as JWK. +// +// Deprecated: use the indigo atcrypto package and multikey string serialization instead func GenerateKeyToFile(fname string) error { raw, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { diff --git a/util/cliutil/key_test.go b/util/cliutil/key_test.go index a4b001d1..51ba8522 100644 --- a/util/cliutil/key_test.go +++ b/util/cliutil/key_test.go @@ -4,8 +4,6 @@ import ( "os" "path/filepath" "testing" - - "github.com/whyrusleeping/go-did" ) func TestKeyGenerationAndLoading(t *testing.T) { @@ -19,12 +17,8 @@ func TestKeyGenerationAndLoading(t *testing.T) { if err != nil { t.Fatal(err) } - key, err := LoadKeyFromFile(fkey) + _, err = LoadKeyFromFile(fkey) if err != nil { t.Fatal(err) } - - if key.Type != did.KeyTypeP256 { - t.Fatalf("unexpected type of the key %s", key.KeyType()) - } } -- 2.51.2 From 48a08495eae0899224ca4132855bbae2b8df814e Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Wed, 18 Feb 2026 19:44:02 -0800 Subject: [PATCH 2/4] gosky: remove DID PLC generation; update cliutil usage There is Go code in the new did-method-plc/go-didplc package which supports PLC generation. For now I don't want to create a dependency loop between the two packages though, so just removing this functionality here. --- cmd/gosky/did.go | 46 +++++----------------------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/cmd/gosky/did.go b/cmd/gosky/did.go index e8ee77bb..c41691d3 100644 --- a/cmd/gosky/did.go +++ b/cmd/gosky/did.go @@ -18,7 +18,6 @@ var didCmd = &cli.Command{ Flags: []cli.Flag{}, Subcommands: []*cli.Command{ didGetCmd, - didCreateCmd, didKeyCmd, }, } @@ -63,45 +62,6 @@ var didGetCmd = &cli.Command{ }, } -var didCreateCmd = &cli.Command{ - Name: "create", - ArgsUsage: ` `, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "recoverydid", - }, - &cli.StringFlag{ - Name: "signingkey", - }, - }, - Action: func(cctx *cli.Context) error { - s := cliutil.GetPLCClient(cctx) - - args, err := needArgs(cctx, "handle", "service") - if err != nil { - return err - } - handle, service := args[0], args[1] - - recoverydid := cctx.String("recoverydid") - - sigkey, err := cliutil.LoadKeyFromFile(cctx.String("signingkey")) - if err != nil { - return err - } - - fmt.Println("KEYDID: ", sigkey.Public().DID()) - - ndid, err := s.CreateDID(context.TODO(), sigkey, recoverydid, handle, service) - if err != nil { - return err - } - - fmt.Println(ndid) - return nil - }, -} - var didKeyCmd = &cli.Command{ Name: "did-key", Flags: []cli.Flag{ @@ -114,7 +74,11 @@ var didKeyCmd = &cli.Command{ if err != nil { return err } - fmt.Println(sigkey.Public().DID()) + pubkey, err := sigkey.PublicKey() + if err != nil { + return err + } + fmt.Println(pubkey.DIDKey()) return nil }, } -- 2.51.2 From 73da25b9947bb7d62916202646602d0eee68162e Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Wed, 18 Feb 2026 19:47:14 -0800 Subject: [PATCH 3/4] remove stale use of did and plc packages --- gen/main.go | 5 ----- testing/utils.go | 20 -------------------- util/cliutil/util.go | 19 +------------------ 3 files changed, 1 insertion(+), 43 deletions(-) diff --git a/gen/main.go b/gen/main.go index 18ee7c1a..03c4a084 100644 --- a/gen/main.go +++ b/gen/main.go @@ -13,7 +13,6 @@ import ( "github.com/bluesky-social/indigo/events" lexutil "github.com/bluesky-social/indigo/lex/util" "github.com/bluesky-social/indigo/mst" - "github.com/bluesky-social/indigo/plc" "github.com/bluesky-social/indigo/repo" "github.com/bluesky-social/indigo/util/labels" @@ -38,10 +37,6 @@ func main() { panic(err) } - if err := genCfg.WriteMapEncodersToFile("plc/cbor_gen.go", "plc", plc.CreateOp{}); err != nil { - panic(err) - } - if err := genCfg.WriteMapEncodersToFile("util/labels/cbor_gen.go", "labels", labels.UnsignedLabel{}); err != nil { panic(err) } diff --git a/testing/utils.go b/testing/utils.go index 7b38bd94..61f85755 100644 --- a/testing/utils.go +++ b/testing/utils.go @@ -7,37 +7,17 @@ import ( "fmt" mathrand "math/rand" "os" - "path/filepath" "strings" - "testing" "time" atproto "github.com/bluesky-social/indigo/api/atproto" bsky "github.com/bluesky-social/indigo/api/bsky" - "github.com/bluesky-social/indigo/plc" "github.com/bluesky-social/indigo/repo" bsutil "github.com/bluesky-social/indigo/util" "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" - - "gorm.io/driver/sqlite" - "gorm.io/gorm" ) -func TestPLC(t *testing.T) *plc.FakeDid { - // TODO: just do in memory... - tdir, err := os.MkdirTemp("", "plcserv") - if err != nil { - t.Fatal(err) - } - - db, err := gorm.Open(sqlite.Open(filepath.Join(tdir, "plc.sqlite"))) - if err != nil { - t.Fatal(err) - } - return plc.NewFakeDid(db) -} - var words = []string{ "cat", "is", diff --git a/util/cliutil/util.go b/util/cliutil/util.go index 37a9cedc..e4a39dde 100644 --- a/util/cliutil/util.go +++ b/util/cliutil/util.go @@ -9,9 +9,8 @@ import ( "strings" "time" - "github.com/bluesky-social/indigo/did" - "github.com/bluesky-social/indigo/plc" "github.com/bluesky-social/indigo/xrpc" + slogGorm "github.com/orandin/slog-gorm" "github.com/urfave/cli/v2" "gorm.io/driver/postgres" @@ -19,22 +18,6 @@ import ( "gorm.io/gorm" ) -func GetDidResolver(cctx *cli.Context) did.Resolver { - mr := did.NewMultiResolver() - mr.AddHandler("plc", &plc.PLCServer{ - Host: cctx.String("plc"), - }) - mr.AddHandler("web", &did.WebResolver{}) - - return mr -} - -func GetPLCClient(cctx *cli.Context) *plc.PLCServer { - return &plc.PLCServer{ - Host: cctx.String("plc"), - } -} - func NewHttpClient() *http.Client { return &http.Client{ Transport: &http.Transport{ -- 2.51.2 From 5b715eb73ebc3052a4de6feb811b3b5629a74b48 Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Wed, 18 Feb 2026 19:47:56 -0800 Subject: [PATCH 4/4] remove deprecated top-level 'did' and 'plc' packages --- did/alias.go | 9 -- did/doc.go | 2 - did/metrics.go | 17 --- did/multi.go | 67 --------- did/web.go | 104 -------------- plc/caching.go | 81 ----------- plc/cbor_gen.go | 348 --------------------------------------------- plc/client.go | 144 ------------------- plc/client_test.go | 109 -------------- plc/doc.go | 2 - plc/fakedid.go | 97 ------------- plc/memcached.go | 90 ------------ plc/metrics.go | 26 ---- plc/plc.go | 14 -- 14 files changed, 1110 deletions(-) delete mode 100644 did/alias.go delete mode 100644 did/doc.go delete mode 100644 did/metrics.go delete mode 100644 did/multi.go delete mode 100644 did/web.go delete mode 100644 plc/caching.go delete mode 100644 plc/cbor_gen.go delete mode 100644 plc/client.go delete mode 100644 plc/client_test.go delete mode 100644 plc/doc.go delete mode 100644 plc/fakedid.go delete mode 100644 plc/memcached.go delete mode 100644 plc/metrics.go delete mode 100644 plc/plc.go diff --git a/did/alias.go b/did/alias.go deleted file mode 100644 index 8d1f2ce3..00000000 --- a/did/alias.go +++ /dev/null @@ -1,9 +0,0 @@ -package did - -import "github.com/whyrusleeping/go-did" - -type Document = did.Document - -type Service = did.Service - -var ParseDID = did.ParseDID diff --git a/did/doc.go b/did/doc.go deleted file mode 100644 index 01a85e0a..00000000 --- a/did/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Deprecated: use github.com/bluesky-social/indigo/atproto/identity for new projects -package did diff --git a/did/metrics.go b/did/metrics.go deleted file mode 100644 index d3c8f0ec..00000000 --- a/did/metrics.go +++ /dev/null @@ -1,17 +0,0 @@ -package did - -import ( - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -var mrResolvedDidsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "multiresolver_resolved_dids_total", - Help: "Total number of DIDs resolved", -}, []string{"resolver"}) - -var mrResolveDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "indigo_multiresolver_resolve_duration_seconds", - Help: "A histogram of resolve latencies", - Buckets: prometheus.ExponentialBuckets(0.001, 2, 15), -}, []string{"resolver"}) diff --git a/did/multi.go b/did/multi.go deleted file mode 100644 index 2c67781f..00000000 --- a/did/multi.go +++ /dev/null @@ -1,67 +0,0 @@ -package did - -import ( - "context" - "fmt" - "time" - - "github.com/whyrusleeping/go-did" -) - -type Resolver interface { - GetDocument(ctx context.Context, didstr string) (*did.Document, error) - FlushCacheFor(did string) -} - -type MultiResolver struct { - handlers map[string]Resolver -} - -func NewMultiResolver() *MultiResolver { - return &MultiResolver{ - handlers: make(map[string]Resolver), - } -} - -func (mr *MultiResolver) AddHandler(method string, res Resolver) { - mr.handlers[method] = res -} - -func (mr *MultiResolver) FlushCacheFor(didstr string) { - pdid, err := did.ParseDID(didstr) - if err != nil { - return - } - - method := pdid.Protocol() - - res, ok := mr.handlers[method] - if !ok { - return - } - - res.FlushCacheFor(didstr) -} - -func (mr *MultiResolver) GetDocument(ctx context.Context, didstr string) (*did.Document, error) { - s := time.Now() - - pdid, err := did.ParseDID(didstr) - if err != nil { - return nil, err - } - - method := pdid.Protocol() - defer func() { - mrResolveDuration.WithLabelValues(method).Observe(time.Since(s).Seconds()) - }() - - res, ok := mr.handlers[method] - if !ok { - return nil, fmt.Errorf("unknown did method: %q", method) - } - - mrResolvedDidsTotal.WithLabelValues(method).Inc() - - return res.GetDocument(ctx, didstr) -} diff --git a/did/web.go b/did/web.go deleted file mode 100644 index baea42e6..00000000 --- a/did/web.go +++ /dev/null @@ -1,104 +0,0 @@ -package did - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - "time" - "unicode" - - "github.com/whyrusleeping/go-did" - "go.opentelemetry.io/otel" -) - -var webDidDefaultTimeout = 5 * time.Second - -type WebResolver struct { - Insecure bool - // TODO: cache? maybe at a different layer - - client http.Client -} - -func (wr *WebResolver) GetDocument(ctx context.Context, didstr string) (*Document, error) { - if wr.client.Timeout == 0 { - wr.client.Timeout = webDidDefaultTimeout - } - ctx, span := otel.Tracer("did").Start(ctx, "didWebGetDocument") - defer span.End() - - pdid, err := did.ParseDID(didstr) - if err != nil { - return nil, err - } - - val := pdid.Value() - if err := checkValidDidWeb(val); err != nil { - return nil, err - } - - proto := "https" - if wr.Insecure { - proto = "http" - } - - resp, err := wr.client.Get(proto + "://" + val + "/.well-known/did.json") - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("fetch did request failed (status %d): %s", resp.StatusCode, resp.Status) - } - - var out did.Document - if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { - return nil, err - } - - return &out, nil -} - -var disallowedTlds = map[string]bool{ - "example": true, - "invalid": true, - "local": true, - "arpa": true, - "onion": true, - "internal": true, -} - -func (wr *WebResolver) FlushCacheFor(did string) { - return -} - -func checkValidDidWeb(val string) error { - // no ports or ipv6 - if strings.Contains(val, ":") { - return fmt.Errorf("did:web resolver does not handle ports or documents at sub-paths") - } - // no trailing '.' - if strings.HasSuffix(val, ".") { - return fmt.Errorf("cannot have trailing period in hostname") - } - - parts := strings.Split(val, ".") - if len(parts) == 1 { - return fmt.Errorf("no bare hostnames (must have subdomain)") - } - - tld := parts[len(parts)-1] - if disallowedTlds[tld] { - return fmt.Errorf("domain cannot use any disallowed TLD") - } - - // disallow tlds that start with numbers - if unicode.IsNumber(rune(tld[0])) { - return fmt.Errorf("TLD cannot start with a number") - } - - return nil -} diff --git a/plc/caching.go b/plc/caching.go deleted file mode 100644 index 7fa2f426..00000000 --- a/plc/caching.go +++ /dev/null @@ -1,81 +0,0 @@ -package plc - -import ( - "context" - "time" - - "github.com/bluesky-social/indigo/did" - arc "github.com/hashicorp/golang-lru/arc/v2" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" -) - -type CachingDidResolver struct { - res did.Resolver - maxAge time.Duration - cache *arc.ARCCache[string, *cachedDoc] -} - -type cachedDoc struct { - cachedAt time.Time - doc *did.Document -} - -func NewCachingDidResolver(res did.Resolver, maxAge time.Duration, size int) *CachingDidResolver { - c, err := arc.NewARC[string, *cachedDoc](size) - if err != nil { - panic(err) - } - - return &CachingDidResolver{ - res: res, - cache: c, - maxAge: maxAge, - } -} - -func (r *CachingDidResolver) FlushCacheFor(didstr string) { - r.cache.Remove(didstr) -} - -func (r *CachingDidResolver) tryCache(did string) (*did.Document, bool) { - cd, ok := r.cache.Get(did) - if !ok { - return nil, false - } - - if time.Since(cd.cachedAt) > r.maxAge { - return nil, false - } - - return cd.doc, true -} - -func (r *CachingDidResolver) putCache(did string, doc *did.Document) { - r.cache.Add(did, &cachedDoc{ - doc: doc, - cachedAt: time.Now(), - }) -} - -func (r *CachingDidResolver) GetDocument(ctx context.Context, didstr string) (*did.Document, error) { - ctx, span := otel.Tracer("cacheResolver").Start(ctx, "getDocument") - defer span.End() - - doc, ok := r.tryCache(didstr) - if ok { - span.SetAttributes(attribute.Bool("cache", true)) - cacheHitsTotal.Inc() - return doc, nil - } - cacheMissesTotal.Inc() - span.SetAttributes(attribute.Bool("cache", false)) - - doc, err := r.res.GetDocument(ctx, didstr) - if err != nil { - return nil, err - } - - r.putCache(didstr, doc) - return doc, nil -} diff --git a/plc/cbor_gen.go b/plc/cbor_gen.go deleted file mode 100644 index 269b07c7..00000000 --- a/plc/cbor_gen.go +++ /dev/null @@ -1,348 +0,0 @@ -// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. - -package plc - -import ( - "fmt" - "io" - "math" - "sort" - - cid "github.com/ipfs/go-cid" - cbg "github.com/whyrusleeping/cbor-gen" - xerrors "golang.org/x/xerrors" -) - -var _ = xerrors.Errorf -var _ = cid.Undef -var _ = math.E -var _ = sort.Sort - -func (t *CreateOp) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - fieldCount := 7 - - if t.Sig == "" { - fieldCount-- - } - - if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { - return err - } - - // t.Sig (string) (string) - if t.Sig != "" { - - if len("sig") > 1000000 { - return xerrors.Errorf("Value in field \"sig\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sig"))); err != nil { - return err - } - if _, err := cw.WriteString(string("sig")); err != nil { - return err - } - - if len(t.Sig) > 1000000 { - return xerrors.Errorf("Value in field t.Sig was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Sig))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Sig)); err != nil { - return err - } - } - - // t.Prev (string) (string) - if len("prev") > 1000000 { - return xerrors.Errorf("Value in field \"prev\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("prev"))); err != nil { - return err - } - if _, err := cw.WriteString(string("prev")); err != nil { - return err - } - - if t.Prev == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if len(*t.Prev) > 1000000 { - return xerrors.Errorf("Value in field t.Prev was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Prev))); err != nil { - return err - } - if _, err := cw.WriteString(string(*t.Prev)); err != nil { - return err - } - } - - // t.Type (string) (string) - if len("type") > 1000000 { - return xerrors.Errorf("Value in field \"type\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("type"))); err != nil { - return err - } - if _, err := cw.WriteString(string("type")); err != nil { - return err - } - - if len(t.Type) > 1000000 { - return xerrors.Errorf("Value in field t.Type was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Type))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Type)); err != nil { - return err - } - - // t.Handle (string) (string) - if len("handle") > 1000000 { - return xerrors.Errorf("Value in field \"handle\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("handle"))); err != nil { - return err - } - if _, err := cw.WriteString(string("handle")); err != nil { - return err - } - - if len(t.Handle) > 1000000 { - return xerrors.Errorf("Value in field t.Handle was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Handle))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Handle)); err != nil { - return err - } - - // t.Service (string) (string) - if len("service") > 1000000 { - return xerrors.Errorf("Value in field \"service\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("service"))); err != nil { - return err - } - if _, err := cw.WriteString(string("service")); err != nil { - return err - } - - if len(t.Service) > 1000000 { - return xerrors.Errorf("Value in field t.Service was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Service))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Service)); err != nil { - return err - } - - // t.SigningKey (string) (string) - if len("signingKey") > 1000000 { - return xerrors.Errorf("Value in field \"signingKey\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("signingKey"))); err != nil { - return err - } - if _, err := cw.WriteString(string("signingKey")); err != nil { - return err - } - - if len(t.SigningKey) > 1000000 { - return xerrors.Errorf("Value in field t.SigningKey was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.SigningKey))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.SigningKey)); err != nil { - return err - } - - // t.RecoveryKey (string) (string) - if len("recoveryKey") > 1000000 { - return xerrors.Errorf("Value in field \"recoveryKey\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("recoveryKey"))); err != nil { - return err - } - if _, err := cw.WriteString(string("recoveryKey")); err != nil { - return err - } - - if len(t.RecoveryKey) > 1000000 { - return xerrors.Errorf("Value in field t.RecoveryKey was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.RecoveryKey))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.RecoveryKey)); err != nil { - return err - } - return nil -} - -func (t *CreateOp) UnmarshalCBOR(r io.Reader) (err error) { - *t = CreateOp{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajMap { - return fmt.Errorf("cbor input should be of type map") - } - - if extra > cbg.MaxLength { - return fmt.Errorf("CreateOp: map struct too large (%d)", extra) - } - - n := extra - - nameBuf := make([]byte, 11) - for i := uint64(0); i < n; i++ { - nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) - if err != nil { - return err - } - - if !ok { - // Field doesn't exist on this type, so ignore it - if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { - return err - } - continue - } - - switch string(nameBuf[:nameLen]) { - // t.Sig (string) (string) - case "sig": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Sig = string(sval) - } - // t.Prev (string) (string) - case "prev": - - { - b, err := cr.ReadByte() - if err != nil { - return err - } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Prev = (*string)(&sval) - } - } - // t.Type (string) (string) - case "type": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Type = string(sval) - } - // t.Handle (string) (string) - case "handle": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Handle = string(sval) - } - // t.Service (string) (string) - case "service": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Service = string(sval) - } - // t.SigningKey (string) (string) - case "signingKey": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.SigningKey = string(sval) - } - // t.RecoveryKey (string) (string) - case "recoveryKey": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.RecoveryKey = string(sval) - } - - default: - // Field doesn't exist on this type, so ignore it - if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { - return err - } - } - } - - return nil -} diff --git a/plc/client.go b/plc/client.go deleted file mode 100644 index b0244d43..00000000 --- a/plc/client.go +++ /dev/null @@ -1,144 +0,0 @@ -package plc - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/base32" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" - - did "github.com/whyrusleeping/go-did" - otel "go.opentelemetry.io/otel" -) - -type PLCServer struct { - Host string - C *http.Client -} - -func (s *PLCServer) GetDocument(ctx context.Context, didstr string) (*did.Document, error) { - ctx, span := otel.Tracer("gosky").Start(ctx, "plsResolveDid") - defer span.End() - - if s.C == nil { - s.C = http.DefaultClient - } - - req, err := http.NewRequest("GET", s.Host+"/"+didstr, nil) - if err != nil { - return nil, err - } - - resp, err := s.C.Do(req.WithContext(ctx)) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("get did request failed (code %d): %s", resp.StatusCode, resp.Status) - } - - var doc did.Document - if err := json.NewDecoder(resp.Body).Decode(&doc); err != nil { - return nil, err - } - - return &doc, nil -} - -func (s *PLCServer) FlushCacheFor(did string) { - return -} - -type CreateOp struct { - Type string `json:"type" cborgen:"type"` - SigningKey string `json:"signingKey" cborgen:"signingKey"` - RecoveryKey string `json:"recoveryKey" cborgen:"recoveryKey"` - Handle string `json:"handle" cborgen:"handle"` - Service string `json:"service" cborgen:"service"` - Prev *string `json:"prev" cborgen:"prev"` - Sig string `json:"sig" cborgen:"sig,omitempty"` -} - -func (s *PLCServer) CreateDID(ctx context.Context, sigkey *did.PrivKey, recovery string, handle string, service string) (string, error) { - if s.C == nil { - s.C = http.DefaultClient - } - - op := CreateOp{ - Type: "create", - SigningKey: sigkey.Public().DID(), - RecoveryKey: recovery, - Handle: handle, - Service: service, - } - - buf := new(bytes.Buffer) - if err := op.MarshalCBOR(buf); err != nil { - return "", err - } - - sig, err := sigkey.Sign(buf.Bytes()) - if err != nil { - return "", err - } - - op.Sig = base64.RawURLEncoding.EncodeToString(sig) - - opdid, err := didForCreateOp(&op) - if err != nil { - return "", err - } - - body, err := json.Marshal(op) - if err != nil { - return "", err - } - - req, err := http.NewRequest("POST", s.Host+"/"+url.QueryEscape(opdid), bytes.NewReader(body)) - if err != nil { - return "", err - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := s.C.Do(req) - if err != nil { - return "", err - } - - defer resp.Body.Close() - - if resp.StatusCode != 200 { - b, _ := io.ReadAll(resp.Body) - fmt.Println(string(b)) - return "", fmt.Errorf("bad response from create call: %d %s", resp.StatusCode, resp.Status) - - } - - return opdid, nil -} - -func (s *PLCServer) UpdateUserHandle(ctx context.Context, did string, handle string) error { - return fmt.Errorf("handle updates not yet implemented") -} - -func didForCreateOp(op *CreateOp) (string, error) { - buf := new(bytes.Buffer) - if err := op.MarshalCBOR(buf); err != nil { - return "", err - } - - h := sha256.Sum256(buf.Bytes()) - enchash := base32.StdEncoding.EncodeToString(h[:]) - enchash = strings.ToLower(enchash) - return "did:plc:" + enchash[:24], nil -} diff --git a/plc/client_test.go b/plc/client_test.go deleted file mode 100644 index f3c9efb0..00000000 --- a/plc/client_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package plc - -import ( - "bytes" - "crypto/ecdsa" - "encoding/base64" - "encoding/json" - "fmt" - "testing" - - "github.com/lestrrat-go/jwx/v2/jwk" - did "github.com/whyrusleeping/go-did" -) - -type testVector struct { - Jwk string - Did string - Op string - SignedOp string - EncodedOp string - Sig string -} - -func TestPLCCreateVector(t *testing.T) { - - tv := testVector{ - Jwk: `{ - "key_ops": [ - "sign" - ], - "ext": true, - "kty": "EC", - "x": "DwHtAjFHuhYWf9RKnE4XgMImEUKA40G0PR8Cbz-GTMY", - "y": "pqny0WfZnm2jHEimqEwg21wvHsWWnqV3E_ofMW_jWuw", - "crv": "P-256", - "d": "elqCGEYj8NB1jXd-I7y4qyy35yZ2PhCOqPW3fM2ToM0" - }`, - Did: "did:key:zDnaeRSYs7c2NpcNA5NRAUqS8DCkLWDyNLnATi28D6w7no7hX", - Op: `{ - "type": "create", - "signingKey": "did:key:zDnaeRSYs7c2NpcNA5NRAUqS8DCkLWDyNLnATi28D6w7no7hX", - "recoveryKey": "did:key:zDnaeRSYs7c2NpcNA5NRAUqS8DCkLWDyNLnATi28D6w7no7hX", - "handle": "why.bsky.social", - "service": "bsky.social", - "prev": null - }`, - SignedOp: `{ - "type": "create", - "signingKey": "did:key:zDnaeRSYs7c2NpcNA5NRAUqS8DCkLWDyNLnATi28D6w7no7hX", - "recoveryKey": "did:key:zDnaeRSYs7c2NpcNA5NRAUqS8DCkLWDyNLnATi28D6w7no7hX", - "handle": "why.bsky.social", - "service": "bsky.social", - "prev": null, - "sig": "e8h6dCx405Z_95cZWWkZtfLgDPvfdXDG9pCZQi1NhduooZgb4d1w-CzahA3J-iNGCCgP3D0O5l997G3vQfxKOA" - }`, - EncodedOp: "pmRwcmV29mR0eXBlZmNyZWF0ZWZoYW5kbGVvd2h5LmJza3kuc29jaWFsZ3NlcnZpY2VrYnNreS5zb2NpYWxqc2lnbmluZ0tleXg5ZGlkOmtleTp6RG5hZVJTWXM3YzJOcGNOQTVOUkFVcVM4RENrTFdEeU5MbkFUaTI4RDZ3N25vN2hYa3JlY292ZXJ5S2V5eDlkaWQ6a2V5OnpEbmFlUlNZczdjMk5wY05BNU5SQVVxUzhEQ2tMV0R5TkxuQVRpMjhENnc3bm83aFg", - Sig: "e8h6dCx405Z_95cZWWkZtfLgDPvfdXDG9pCZQi1NhduooZgb4d1w-CzahA3J-iNGCCgP3D0O5l997G3vQfxKOA", - } - - var op CreateOp - if err := json.Unmarshal([]byte(tv.Op), &op); err != nil { - t.Fatal(err) - } - - buf := new(bytes.Buffer) - if err := op.MarshalCBOR(buf); err != nil { - t.Fatal(err) - } - - exp, err := base64.RawURLEncoding.DecodeString(tv.EncodedOp) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(buf.Bytes(), exp) { - fmt.Printf("%x\n", buf.Bytes()) - fmt.Printf("%x\n", exp) - t.Fatal("encoding mismatched") - } - - k, err := jwk.ParseKey([]byte(tv.Jwk)) - if err != nil { - t.Fatal(err) - } - - var spk ecdsa.PrivateKey - if err := k.Raw(&spk); err != nil { - t.Fatal(err) - } - - mk := did.PrivKey{ - Raw: &spk, - Type: did.KeyTypeP256, - } - - if mk.Public().DID() != tv.Did { - t.Fatal("keys generated different DIDs") - } - - mysig, err := mk.Sign(buf.Bytes()) - if err != nil { - t.Fatal(err) - } - - fmt.Println(base64.RawURLEncoding.DecodeString(tv.Sig)) - fmt.Println(base64.RawURLEncoding.EncodeToString(mysig)) - fmt.Println(tv.Sig) - -} diff --git a/plc/doc.go b/plc/doc.go deleted file mode 100644 index 454e34e1..00000000 --- a/plc/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Deprecated: use github.com/bluesky-social/indigo/atproto/identity for new projects -package plc diff --git a/plc/fakedid.go b/plc/fakedid.go deleted file mode 100644 index 5162ca8a..00000000 --- a/plc/fakedid.go +++ /dev/null @@ -1,97 +0,0 @@ -package plc - -import ( - "context" - "crypto/rand" - "encoding/hex" - - "github.com/whyrusleeping/go-did" - "gorm.io/gorm" -) - -type FakeDidMapping struct { - gorm.Model - Handle string - Did string `gorm:"index"` - Service string - KeyType string - PubKeyMbase string -} - -type FakeDid struct { - db *gorm.DB -} - -func NewFakeDid(db *gorm.DB) *FakeDid { - db.AutoMigrate(&FakeDidMapping{}) - return &FakeDid{db} -} - -func (fd *FakeDid) GetDocument(ctx context.Context, udid string) (*did.Document, error) { - var rec FakeDidMapping - if err := fd.db.First(&rec, "did = ?", udid).Error; err != nil { - return nil, err - } - - d, err := did.ParseDID(rec.Did) - if err != nil { - panic(err) - } - - return &did.Document{ - Context: []string{}, - - ID: d, - - AlsoKnownAs: []string{"https://" + rec.Handle}, - - //Authentication []interface{} `json:"authentication"` - - VerificationMethod: []did.VerificationMethod{ - did.VerificationMethod{ - ID: "#signingKey", - Type: rec.KeyType, - PublicKeyMultibase: &rec.PubKeyMbase, - Controller: rec.Did, - }, - }, - - Service: []did.Service{ - did.Service{ - //ID: "", - Type: "pds", - ServiceEndpoint: "http://" + rec.Service, - }, - }, - }, nil -} - -func (fd *FakeDid) FlushCacheFor(did string) { - return -} - -func (fd *FakeDid) CreateDID(ctx context.Context, sigkey *did.PrivKey, recovery string, handle string, service string) (string, error) { - buf := make([]byte, 8) - rand.Read(buf) - d := "did:plc:" + hex.EncodeToString(buf) - - if err := fd.db.Create(&FakeDidMapping{ - Handle: handle, - Did: d, - Service: service, - PubKeyMbase: sigkey.Public().MultibaseString(), - KeyType: sigkey.KeyType(), - }).Error; err != nil { - return "", err - } - - return d, nil -} - -func (fd *FakeDid) UpdateUserHandle(ctx context.Context, did string, nhandle string) error { - if err := fd.db.Model(FakeDidMapping{}).Where("did = ?", did).UpdateColumn("handle", nhandle).Error; err != nil { - return err - } - - return nil -} diff --git a/plc/memcached.go b/plc/memcached.go deleted file mode 100644 index 50b0e292..00000000 --- a/plc/memcached.go +++ /dev/null @@ -1,90 +0,0 @@ -package plc - -import ( - "context" - "encoding/json" - "github.com/bradfitz/gomemcache/memcache" - "go.opentelemetry.io/otel/attribute" - "time" - - "github.com/bluesky-social/indigo/did" - "go.opentelemetry.io/otel" -) - -type MemcachedDidResolver struct { - mcd *memcache.Client - res did.Resolver - maxAge int32 -} - -func NewMemcachedDidResolver(res did.Resolver, maxAge time.Duration, servers []string) *MemcachedDidResolver { - expiry := int32(0) - if maxAge.Seconds() > (30 * 24 * 60 * 60) { - // clamp expiry at 30 days minus a minute for memcached - expiry = (30 * 24 * 60 * 60) - 60 - } else { - expiry = int32(maxAge.Seconds()) - } - client := memcache.New(servers...) - return &MemcachedDidResolver{ - mcd: client, - res: res, - maxAge: expiry, - } -} - -func (r *MemcachedDidResolver) FlushCacheFor(didstr string) { - r.mcd.Delete(didstr) - r.res.FlushCacheFor(didstr) -} - -func (r *MemcachedDidResolver) tryCache(didstr string) (*did.Document, bool) { - ob, err := r.mcd.Get(didstr) - if (ob == nil) || (err != nil) { - return nil, false - } - var doc did.Document - err = json.Unmarshal(ob.Value, &doc) - if err != nil { - // TODO: log error? - return nil, false - } - - return &doc, true -} - -func (r *MemcachedDidResolver) putCache(did string, doc *did.Document) { - blob, err := json.Marshal(doc) - if err != nil { - // TODO: log error - return - } - item := memcache.Item{ - Key: did, - Value: blob, - Expiration: int32(r.maxAge), - } - r.mcd.Set(&item) -} - -func (r *MemcachedDidResolver) GetDocument(ctx context.Context, didstr string) (*did.Document, error) { - ctx, span := otel.Tracer("cacheResolver").Start(ctx, "getDocument") - defer span.End() - - doc, ok := r.tryCache(didstr) - if ok { - span.SetAttributes(attribute.Bool("cache", true)) - memcacheHitsTotal.Inc() - return doc, nil - } - memcacheMissesTotal.Inc() - span.SetAttributes(attribute.Bool("cache", false)) - - doc, err := r.res.GetDocument(ctx, didstr) - if err != nil { - return nil, err - } - - r.putCache(didstr, doc) - return doc, nil -} diff --git a/plc/metrics.go b/plc/metrics.go deleted file mode 100644 index ea2af202..00000000 --- a/plc/metrics.go +++ /dev/null @@ -1,26 +0,0 @@ -package plc - -import ( - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" -) - -var cacheHitsTotal = promauto.NewCounter(prometheus.CounterOpts{ - Name: "plc_cache_hits_total", - Help: "Total number of cache hits", -}) - -var cacheMissesTotal = promauto.NewCounter(prometheus.CounterOpts{ - Name: "plc_cache_misses_total", - Help: "Total number of cache misses", -}) - -var memcacheHitsTotal = promauto.NewCounter(prometheus.CounterOpts{ - Name: "plc_memcache_hits_total", - Help: "Total number of cache hits", -}) - -var memcacheMissesTotal = promauto.NewCounter(prometheus.CounterOpts{ - Name: "plc_memcache_misses_total", - Help: "Total number of cache misses", -}) diff --git a/plc/plc.go b/plc/plc.go deleted file mode 100644 index e8bb9720..00000000 --- a/plc/plc.go +++ /dev/null @@ -1,14 +0,0 @@ -package plc - -import ( - "context" - - didres "github.com/bluesky-social/indigo/did" - "github.com/whyrusleeping/go-did" -) - -type PLCClient interface { - didres.Resolver - CreateDID(ctx context.Context, sigkey *did.PrivKey, recovery string, handle string, service string) (string, error) - UpdateUserHandle(ctx context.Context, didstr string, nhandle string) error -} -- 2.51.2