Live video on the AT Protocol
1package api
2
3import (
4 "context"
5 "crypto"
6 "fmt"
7
8 atcrypto "github.com/bluesky-social/indigo/atproto/crypto"
9 "github.com/decred/dcrd/dcrec/secp256k1"
10 "github.com/mr-tron/base58"
11 "stream.place/streamplace/pkg/atproto"
12 "stream.place/streamplace/pkg/log"
13 "stream.place/streamplace/pkg/media"
14)
15
16func (a *StreamplaceAPI) MakeMediaSigner(ctx context.Context, keyStr string) (media.MediaSigner, error) {
17 if len(keyStr) < 2 || keyStr[0] != 'z' {
18 return nil, fmt.Errorf("invalid authorization key (not a multibase base58btc string)")
19 }
20
21 var addrBytes []byte
22 var didBytes []byte
23 priv, err := atcrypto.ParsePrivateMultibase(keyStr)
24 if err == nil {
25 addrBytes = priv.Bytes()
26 } else {
27 decoded, err := base58.Decode(keyStr[1:])
28 if err != nil {
29 return nil, fmt.Errorf("invalid authorization key (not a base58btc string)")
30 }
31 addrBytes = decoded[:32]
32 didBytes = decoded[32:]
33 priv, err = atcrypto.ParsePrivateBytesK256(addrBytes)
34 if err != nil {
35 return nil, fmt.Errorf("invalid authorization key (not valid atproto): %w", err)
36 }
37 }
38
39 key, _ := secp256k1.PrivKeyFromBytes(addrBytes)
40 if key == nil {
41 return nil, fmt.Errorf("invalid authorization key (not valid secp256k1)")
42 }
43 var signer crypto.Signer = key.ToECDSA()
44 pub, err := priv.PublicKey()
45 if err != nil {
46 return nil, fmt.Errorf("invalid authorization key (could not parse as atproto): %w", err)
47 }
48
49 did := string(didBytes)
50
51 if did != "" {
52 repo, err := a.ATSync.SyncBlueskyRepo(ctx, did, a.Model)
53 if err != nil {
54 return nil, fmt.Errorf("could not resolve streamplace key: %w", err)
55 }
56 err = a.CLI.StreamIsAllowed(repo.DID)
57 if err != nil {
58 return nil, fmt.Errorf("user is not allowed to stream: %w", err)
59 }
60 signingKey, err := a.Model.GetSigningKey(ctx, pub.DIDKey(), repo.DID)
61 if err != nil {
62 return nil, fmt.Errorf("signing key not found: %w", err)
63 }
64 if signingKey == nil {
65 return nil, fmt.Errorf("signing key not found")
66 }
67 } else {
68 atkey, err := atproto.ParsePubKey(signer.Public())
69 if err != nil {
70 return nil, fmt.Errorf("invalid authorization key (not valid secp256k1): %w", err)
71 }
72 did = atkey.DIDKey()
73 err = a.CLI.StreamIsAllowed(did)
74 if err != nil {
75 return nil, fmt.Errorf("user is not allowed to stream: %w", err)
76 }
77 }
78
79 ctx = log.WithLogValues(ctx, "did", did)
80 err = a.checkBanned(ctx, did)
81 if err != nil {
82 return nil, err
83 }
84
85 mediaSigner, err := media.MakeMediaSigner(ctx, a.CLI, did, signer, a.Model)
86 if err != nil {
87 return nil, fmt.Errorf("invalid authorization key (not valid secp256k1): %w", err)
88 }
89
90 return mediaSigner, nil
91}
92
93func (a *StreamplaceAPI) checkBanned(ctx context.Context, did string) error {
94 labels, err := a.Model.GetActiveLabels(did)
95 if err != nil {
96 return fmt.Errorf("failed to get active labels: %w", err)
97 }
98 if atproto.IsBanned(labels...) {
99 log.Error(ctx, "user is banned", "did", did)
100 return fmt.Errorf("user is banned")
101 }
102 return nil
103}