-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsip.go
140 lines (125 loc) · 3.92 KB
/
sip.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package attestation
import (
"aidanwoods.dev/go-paseto"
"encoding/json"
"errors"
"github.com/authenticvision/attestation-go/paserk"
"github.com/authenticvision/httputil-go"
"github.com/authenticvision/httputil-go/httplog"
"go.uber.org/zap"
"net/http"
)
const Version = 4
type Result string
const (
ResultAuthentic Result = "AUTHENTIC"
ResultCounterfeit Result = "COUNTERFEIT"
ResultInconclusive Result = "INCONCLUSIVE"
)
type Reason string
const (
ReasonNone Reason = ""
ReasonBlacklisted Reason = "BLACKLISTED"
ReasonInactive Reason = "INACTIVE"
ReasonSignature Reason = "SIGNATURE"
ReasonVoid Reason = "VOID"
ReasonDisplay Reason = "DISPLAY"
)
type Location struct {
Latitude float64 `json:"lat"`
Longitude float64 `json:"lon"`
}
type Token struct {
Version int `json:"_v"`
Audience string `json:"aud"`
Expiration RFC3339Time `json:"exp"`
IssuedAt RFC3339Time `json:"iat"`
SessionID string `json:"jti"`
SLID SLID36 `json:"slid"`
GTIN string `json:"gtin,omitempty"`
Result Result `json:"result"`
Reason Reason `json:"reason"`
Location *Location `json:"location,omitempty"`
ExtRefs []json.RawMessage `json:"extrefs,omitempty"`
}
type Middleware struct {
KeyStore *KeyStore
Required bool
}
func NewMiddleware() *Middleware {
return &Middleware{KeyStore: SharedKeyStore, Required: true}
}
func (s *Middleware) Middleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.URL.Query().Get("av_sip4")
if token == "" {
if s.Required {
http.Error(w, "This server serves responses for Authentic Vision Mobile SDK "+
"applications and cannot be used directly. The av_sip4 query parameter is required.",
http.StatusBadRequest)
return
} else {
handler.ServeHTTP(w, r)
return
}
}
log := httplog.FromRequest(r)
log = log.With(zap.String("sip4_token", token))
// SIP token validation
// expiration checks are handled by the default rule added in NewParser
var err error
var claims Token
p := paseto.NewParser()
footer, err := p.UnsafeParseFooter(paseto.V4Public, token)
if err != nil {
log.Warn("SIP token parsing failed", zap.Error(err))
http.Error(w, "SIP token parsing failed", http.StatusBadRequest)
return
}
kid, err := paserk.ParseKeyIDFooter(string(footer))
if err != nil {
log.Warn("SIP token footer parsing failed", zap.Error(err))
http.Error(w, "SIP token footer parsing failed", http.StatusBadRequest)
return
}
publicKey, err := s.KeyStore.GetPublicKey(kid)
if errors.Is(err, ErrNoSuchKey) {
log.Warn("no such SIPv4 key", zap.Error(err))
http.Error(w, "SIP key not trusted", http.StatusForbidden)
return
} else if err != nil {
log.Warn("could not retrieve SIPv4 key", zap.Error(err))
http.Error(w, "SIP key unavailable", http.StatusServiceUnavailable)
return
}
t, err := p.ParseV4Public(publicKey, token, nil)
if err != nil {
log.Warn("SIP token decryption failed", zap.Error(err))
http.Error(w, "SIP token invalid", http.StatusForbidden)
return
}
if err := json.Unmarshal(t.ClaimsJSON(), &claims); err != nil {
log.Warn("claims unmarshalling failed", zap.Error(err))
http.Error(w, "SIP token invalid", http.StatusInternalServerError)
return
}
if claims.Version != Version {
log.Warn("SIP token has wrong version", zap.Int("expected", 4),
zap.Int("got", claims.Version), zap.Error(err))
http.Error(w, "SIP token has wrong version", http.StatusBadRequest)
return
}
log = log.With(zap.String("slid", string(claims.SLID)))
r = httputil.RequestWithValue(r, log)
r = httputil.RequestWithValue(r, &claims)
handler.ServeHTTP(w, r)
})
}
func FromRequest(r *http.Request) *Token {
var tag *Token
if v, ok := r.Context().Value(tag).(*Token); ok {
return v
} else {
return nil
}
}