1package auth
2
3import (
4 "crypto"
5
6 atcrypto "github.com/bluesky-social/indigo/atproto/crypto"
7 "github.com/golang-jwt/jwt/v5"
8)
9
10var (
11 signingMethodES256K *signingMethodAtproto
12 signingMethodES256 *signingMethodAtproto
13 supportedAlgs []string
14)
15
16// Implementation of jwt.SigningMethod for the `atproto/crypto` types.
17type signingMethodAtproto struct {
18 alg string
19 hash crypto.Hash
20 toOutSig toOutSig
21 sigLen int
22}
23
24type toOutSig func(sig []byte) []byte
25
26func init() {
27 // tells JWT library to serialize 'aud' as regular string, not array of strings (when signing)
28 jwt.MarshalSingleStringAsArray = false
29
30 signingMethodES256K = &signingMethodAtproto{
31 alg: "ES256K",
32 hash: crypto.SHA256,
33 toOutSig: toES256K,
34 sigLen: 64,
35 }
36 jwt.RegisterSigningMethod(signingMethodES256K.Alg(), func() jwt.SigningMethod {
37 return signingMethodES256K
38 })
39 signingMethodES256 = &signingMethodAtproto{
40 alg: "ES256",
41 hash: crypto.SHA256,
42 toOutSig: toES256,
43 sigLen: 64,
44 }
45 jwt.RegisterSigningMethod(signingMethodES256.Alg(), func() jwt.SigningMethod {
46 return signingMethodES256
47 })
48 supportedAlgs = []string{signingMethodES256K.Alg(), signingMethodES256.Alg()}
49}
50
51func (sm *signingMethodAtproto) Verify(signingString string, sig []byte, key interface{}) error {
52 pub, ok := key.(atcrypto.PublicKey)
53 if !ok {
54 return jwt.ErrInvalidKeyType
55 }
56
57 if !sm.hash.Available() {
58 return jwt.ErrHashUnavailable
59 }
60
61 if len(sig) != sm.sigLen {
62 return jwt.ErrTokenSignatureInvalid
63 }
64
65 // NOTE: important to use using "lenient" variant here
66 return pub.HashAndVerifyLenient([]byte(signingString), sig)
67}
68
69func (sm *signingMethodAtproto) Sign(signingString string, key interface{}) ([]byte, error) {
70 priv, ok := key.(atcrypto.PrivateKey)
71 if !ok {
72 return nil, jwt.ErrInvalidKeyType
73 }
74
75 return priv.HashAndSign([]byte(signingString))
76}
77
78func (sm *signingMethodAtproto) Alg() string {
79 return sm.alg
80}
81
82func toES256K(sig []byte) []byte {
83 return sig[:64]
84}
85
86func toES256(sig []byte) []byte {
87 return sig[:64]
88}