aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2021-11-22 18:39:22 -0800
committerMike Crute <mike@crute.us>2021-11-22 18:39:22 -0800
commit8bb307c11b51cb0fda3df374bdc9c0958b7d522e (patch)
treee63a8230e8e777a586c4e15bd26ac720699f01d3
parent4f457596a2fa991b5914d4c368697c659db9d1e7 (diff)
downloadcloud-identity-broker-8bb307c11b51cb0fda3df374bdc9c0958b7d522e.tar.bz2
cloud-identity-broker-8bb307c11b51cb0fda3df374bdc9c0958b7d522e.tar.xz
cloud-identity-broker-8bb307c11b51cb0fda3df374bdc9c0958b7d522e.zip
Session keys can be JSON serialized
-rw-r--r--app/models/session_key.go207
1 files changed, 169 insertions, 38 deletions
diff --git a/app/models/session_key.go b/app/models/session_key.go
index b1fdc90..c8b327e 100644
--- a/app/models/session_key.go
+++ b/app/models/session_key.go
@@ -8,6 +8,7 @@ import (
8 "crypto/x509" 8 "crypto/x509"
9 "encoding/base64" 9 "encoding/base64"
10 "encoding/hex" 10 "encoding/hex"
11 "encoding/json"
11 "fmt" 12 "fmt"
12 "time" 13 "time"
13 14
@@ -18,8 +19,16 @@ import (
18// that will be stored on the user's record in the user store. These keys are 19// that will be stored on the user's record in the user store. These keys are
19// used for signing authentication JWTs. 20// used for signing authentication JWTs.
20// 21//
21// This object is designed to be serialized to BSON. Other serializations can 22// This object is designed to be serialized to and from BSON and JSON. Other
22// be added in the future as needed. 23// serializations can be added in the future as needed.
24//
25// The ExposePrivateKeysInJSON controls how JSON serialization of this struct
26// works. When the field is set to false (the default) then serialization into
27// JSON will never encode a private key, but may encode a public key. If this
28// is set to true then the private key will be encoded into the JSON value and
29// not the public key. SETTING THIS TO TRUE AND EXPOSING THE RESULTS TO THE
30// USER IS A SECURITY ERROR so this should normally not be changed. This value
31// of this field will never be persisted in any form.
23// 32//
24// There are two flavors of this record. A record with a private key (which 33// There are two flavors of this record. A record with a private key (which
25// implies a public key) is a key that the service generated and is used by the 34// implies a public key) is a key that the service generated and is used by the
@@ -36,13 +45,14 @@ import (
36// private key and the service will validate the signature with the public key. 45// private key and the service will validate the signature with the public key.
37// These keys (as of Nov 2021) do not expire, though they can be revoked. 46// These keys (as of Nov 2021) do not expire, though they can be revoked.
38type SessionKey struct { 47type SessionKey struct {
39 KeyId string 48 KeyId string
40 Description string 49 Description string
41 Revoked *time.Time 50 Revoked *time.Time
42 NotAfter *time.Time 51 NotAfter *time.Time
43 NotBefore *time.Time 52 NotBefore *time.Time
44 PublicKey crypto.PublicKey 53 PublicKey crypto.PublicKey
45 PrivateKey *ecdsa.PrivateKey 54 PrivateKey *ecdsa.PrivateKey
55 ExposePrivateKeysInJSON bool `"-" json:"-" bson:"-"`
46} 56}
47 57
48func GenerateSessionKey(ttl time.Duration) (*SessionKey, error) { 58func GenerateSessionKey(ttl time.Duration) (*SessionKey, error) {
@@ -108,12 +118,156 @@ func (s *SessionKey) IsValid() bool {
108 return true 118 return true
109} 119}
110 120
121// MarshalPrivateKey marshals the private key to a X509 encoded base64 string
122func (s *SessionKey) MarshalPrivateKey() (string, error) {
123 priv, err := x509.MarshalECPrivateKey(s.PrivateKey)
124 if err != nil {
125 return "", err
126 }
127 return base64.StdEncoding.EncodeToString(priv), nil
128}
129
130// MarshalPublicKey marshals the public key to an X509 encoded base64 string
131func (s *SessionKey) MarshalPublicKey() (string, error) {
132 pub, err := x509.MarshalPKIXPublicKey(s.PublicKey)
133 if err != nil {
134 return "", err
135 }
136 return base64.StdEncoding.EncodeToString(pub), nil
137}
138
139// UnmarshalPrivateKey unmarshals the private key from a base64 encoded X509
140// string into the public and private key fields.
141func (s *SessionKey) UnmarshalPrivateKey(k string) error {
142 privb, err := base64.StdEncoding.DecodeString(k)
143 if err != nil {
144 return err
145 }
146
147 priv, err := x509.ParseECPrivateKey(privb)
148 if err != nil {
149 return err
150 }
151
152 s.PrivateKey = priv
153 s.PublicKey = priv.Public()
154
155 return nil
156}
157
158// UnmarshalPublicKey unmarshals the public key from a base64 encoded X509
159// string into the public key field.
160func (s *SessionKey) UnmarshalPublicKey(k string) error {
161 pubb, err := base64.StdEncoding.DecodeString(k)
162 if err != nil {
163 return err
164 }
165
166 pubp, err := x509.ParsePKIXPublicKey(pubb)
167 if err != nil {
168 return err
169 }
170
171 pub, ok := pubp.(*ecdsa.PublicKey)
172 if !ok {
173 return fmt.Errorf("Failed to convert public key to *ecdsa.PublicKey")
174 }
175
176 s.PublicKey = pub
177 return nil
178}
179
180// MarshalJSON marshals a struct to JSON
181//
182// This method will have different behavior if the ExposePrivateKeysInJSON
183// field is set in the struct (the default is false). If this field is set to
184// true the private keys will be exposed in the JSON results. If it is false
185// then private keys will not be exposed. The ExposePrivateKeysInJSON itself
186// will never be serialized.
187func (s *SessionKey) MarshalJSON() ([]byte, error) {
188 var err error
189 var privKey *string
190 var pub, priv string
191
192 if s.PrivateKey != nil && s.ExposePrivateKeysInJSON {
193 priv, err = s.MarshalPrivateKey()
194 if err != nil {
195 return nil, err
196 }
197 if priv != "" {
198 privKey = &priv
199 }
200 }
201
202 // If there's a private key and a public key set, and exposing the private
203 // key is allowed, then just save the private key. The private key already
204 // contains a copy of the public key.
205 if s.PublicKey != nil && (s.PrivateKey == nil || !s.ExposePrivateKeysInJSON) {
206 pub, err = s.MarshalPublicKey()
207 if err != nil {
208 return nil, err
209 }
210 }
211
212 return json.Marshal(struct {
213 KeyId string `json:"key_id"`
214 Revoked *time.Time `json:"revoked,omitempty"`
215 NotAfter *time.Time `json:"not_after"`
216 NotBefore *time.Time `json:"not_before"`
217 PublicKey string `json:"public_key"`
218 PrivateKey *string `json:"private_key,omitempty"`
219 }{
220 s.KeyId,
221 s.Revoked, s.NotAfter, s.NotBefore,
222 pub, privKey,
223 })
224}
225
226// UnmarshalJSON unmarshals a struct from JSON.
227//
228// This method does attempt to unmarshal private keys.
229func (s *SessionKey) UnmarshalJSON(d []byte) error {
230 v := struct {
231 KeyId string `json:"key_id"`
232 Revoked *time.Time `json:"revoked,omitempty"`
233 NotAfter *time.Time `json:"not_after"`
234 NotBefore *time.Time `json:"not_before"`
235 PublicKey string `json:"public_key"`
236 PrivateKey string `json:"private_key"`
237 }{}
238 if err := json.Unmarshal(d, &v); err != nil {
239 return err
240 }
241
242 s.KeyId = v.KeyId
243 s.Revoked = v.Revoked
244 s.NotAfter = v.NotAfter
245 s.NotBefore = v.NotBefore
246
247 if v.PrivateKey != "" {
248 if err := s.UnmarshalPrivateKey(v.PrivateKey); err != nil {
249 return err
250 }
251 }
252
253 // If there was a private key then the public key was already set by
254 // decoding that private key. No need to do this a second time (also it's
255 // rather unlikely that both would be set).
256 if v.PublicKey != "" && s.PublicKey == nil {
257 if err := s.UnmarshalPublicKey(v.PublicKey); err != nil {
258 return err
259 }
260 }
261
262 return nil
263}
264
111func (s *SessionKey) MarshalBSON() ([]byte, error) { 265func (s *SessionKey) MarshalBSON() ([]byte, error) {
112 var err error 266 var err error
113 var pub, priv []byte 267 var pub, priv string
114 268
115 if s.PrivateKey != nil { 269 if s.PrivateKey != nil {
116 priv, err = x509.MarshalECPrivateKey(s.PrivateKey) 270 priv, err = s.MarshalPrivateKey()
117 if err != nil { 271 if err != nil {
118 return nil, err 272 return nil, err
119 } 273 }
@@ -122,7 +276,7 @@ func (s *SessionKey) MarshalBSON() ([]byte, error) {
122 // If there's a private key and a public key set then just save the private 276 // If there's a private key and a public key set then just save the private
123 // key. The private key already contains a copy of the public key. 277 // key. The private key already contains a copy of the public key.
124 if s.PublicKey != nil && s.PrivateKey == nil { 278 if s.PublicKey != nil && s.PrivateKey == nil {
125 pub, err = x509.MarshalPKIXPublicKey(s.PublicKey) 279 pub, err = s.MarshalPublicKey()
126 if err != nil { 280 if err != nil {
127 return nil, err 281 return nil, err
128 } 282 }
@@ -138,8 +292,7 @@ func (s *SessionKey) MarshalBSON() ([]byte, error) {
138 }{ 292 }{
139 s.KeyId, 293 s.KeyId,
140 s.Revoked, s.NotAfter, s.NotBefore, 294 s.Revoked, s.NotAfter, s.NotBefore,
141 base64.StdEncoding.EncodeToString(pub), 295 pub, priv,
142 base64.StdEncoding.EncodeToString(priv),
143 }) 296 })
144} 297}
145 298
@@ -162,40 +315,18 @@ func (s *SessionKey) UnmarshalBSON(d []byte) error {
162 s.NotBefore = v.NotBefore 315 s.NotBefore = v.NotBefore
163 316
164 if v.PrivateKey != "" { 317 if v.PrivateKey != "" {
165 privb, err := base64.StdEncoding.DecodeString(v.PrivateKey) 318 if err := s.UnmarshalPrivateKey(v.PrivateKey); err != nil {
166 if err != nil {
167 return err 319 return err
168 } 320 }
169
170 priv, err := x509.ParseECPrivateKey(privb)
171 if err != nil {
172 return err
173 }
174
175 s.PrivateKey = priv
176 s.PublicKey = priv.Public()
177 } 321 }
178 322
179 // If there was a private key then the public key was already set by 323 // If there was a private key then the public key was already set by
180 // decoding that private key. No need to do this a second time (also it's 324 // decoding that private key. No need to do this a second time (also it's
181 // rather unlikely that both would be set). 325 // rather unlikely that both would be set).
182 if v.PublicKey != "" && s.PublicKey == nil { 326 if v.PublicKey != "" && s.PublicKey == nil {
183 pubb, err := base64.StdEncoding.DecodeString(v.PublicKey) 327 if err := s.UnmarshalPublicKey(v.PublicKey); err != nil {
184 if err != nil {
185 return err 328 return err
186 } 329 }
187
188 pubp, err := x509.ParsePKIXPublicKey(pubb)
189 if err != nil {
190 return err
191 }
192
193 pub, ok := pubp.(*ecdsa.PublicKey)
194 if !ok {
195 return fmt.Errorf("Failed to convert public key to *ecdsa.PublicKey")
196 }
197
198 s.PublicKey = pub
199 } 330 }
200 331
201 return nil 332 return nil