fork of indigo with slightly nicer lexgen
at main 3.1 kB view raw
1package plc 2 3import ( 4 "bytes" 5 "context" 6 "crypto/sha256" 7 "encoding/base32" 8 "encoding/base64" 9 "encoding/json" 10 "fmt" 11 "io" 12 "net/http" 13 "net/url" 14 "strings" 15 16 did "github.com/whyrusleeping/go-did" 17 otel "go.opentelemetry.io/otel" 18) 19 20type PLCServer struct { 21 Host string 22 C *http.Client 23} 24 25func (s *PLCServer) GetDocument(ctx context.Context, didstr string) (*did.Document, error) { 26 ctx, span := otel.Tracer("gosky").Start(ctx, "plsResolveDid") 27 defer span.End() 28 29 if s.C == nil { 30 s.C = http.DefaultClient 31 } 32 33 req, err := http.NewRequest("GET", s.Host+"/"+didstr, nil) 34 if err != nil { 35 return nil, err 36 } 37 38 resp, err := s.C.Do(req.WithContext(ctx)) 39 if err != nil { 40 return nil, err 41 } 42 43 defer resp.Body.Close() 44 45 if resp.StatusCode != 200 { 46 return nil, fmt.Errorf("get did request failed (code %d): %s", resp.StatusCode, resp.Status) 47 } 48 49 var doc did.Document 50 if err := json.NewDecoder(resp.Body).Decode(&doc); err != nil { 51 return nil, err 52 } 53 54 return &doc, nil 55} 56 57func (s *PLCServer) FlushCacheFor(did string) { 58 return 59} 60 61type CreateOp struct { 62 Type string `json:"type" cborgen:"type"` 63 SigningKey string `json:"signingKey" cborgen:"signingKey"` 64 RecoveryKey string `json:"recoveryKey" cborgen:"recoveryKey"` 65 Handle string `json:"handle" cborgen:"handle"` 66 Service string `json:"service" cborgen:"service"` 67 Prev *string `json:"prev" cborgen:"prev"` 68 Sig string `json:"sig" cborgen:"sig,omitempty"` 69} 70 71func (s *PLCServer) CreateDID(ctx context.Context, sigkey *did.PrivKey, recovery string, handle string, service string) (string, error) { 72 if s.C == nil { 73 s.C = http.DefaultClient 74 } 75 76 op := CreateOp{ 77 Type: "create", 78 SigningKey: sigkey.Public().DID(), 79 RecoveryKey: recovery, 80 Handle: handle, 81 Service: service, 82 } 83 84 buf := new(bytes.Buffer) 85 if err := op.MarshalCBOR(buf); err != nil { 86 return "", err 87 } 88 89 sig, err := sigkey.Sign(buf.Bytes()) 90 if err != nil { 91 return "", err 92 } 93 94 op.Sig = base64.RawURLEncoding.EncodeToString(sig) 95 96 opdid, err := didForCreateOp(&op) 97 if err != nil { 98 return "", err 99 } 100 101 body, err := json.Marshal(op) 102 if err != nil { 103 return "", err 104 } 105 106 req, err := http.NewRequest("POST", s.Host+"/"+url.QueryEscape(opdid), bytes.NewReader(body)) 107 if err != nil { 108 return "", err 109 } 110 111 req.Header.Set("Content-Type", "application/json") 112 113 resp, err := s.C.Do(req) 114 if err != nil { 115 return "", err 116 } 117 118 defer resp.Body.Close() 119 120 if resp.StatusCode != 200 { 121 b, _ := io.ReadAll(resp.Body) 122 fmt.Println(string(b)) 123 return "", fmt.Errorf("bad response from create call: %d %s", resp.StatusCode, resp.Status) 124 125 } 126 127 return opdid, nil 128} 129 130func (s *PLCServer) UpdateUserHandle(ctx context.Context, did string, handle string) error { 131 return fmt.Errorf("handle updates not yet implemented") 132} 133 134func didForCreateOp(op *CreateOp) (string, error) { 135 buf := new(bytes.Buffer) 136 if err := op.MarshalCBOR(buf); err != nil { 137 return "", err 138 } 139 140 h := sha256.Sum256(buf.Bytes()) 141 enchash := base32.StdEncoding.EncodeToString(h[:]) 142 enchash = strings.ToLower(enchash) 143 return "did:plc:" + enchash[:24], nil 144}