fix: Add the token's rootcert public key to the list of known keys

- Add Unit tests for `token.newAccessController`
  + Implemented swappable implementations for `token.getRootCerts` and
    `getJwks` to unit test their behavior over the accessController
    struct.

- Use RFC7638 [0] mechanics to compute the KeyID of the rootcertbundle
  provided in the token auth config.

- Extends token authentication docs:
  + Extend `jwt.md` write up on JWT headers & JWT Validation
  + Updated old reference to a draft that's now RFC7515.
  + Extended the JWT validation steps with the JWT Header validation.
  + Reference `jwt.md` in `token.md`

[0]: https://datatracker.ietf.org/doc/html/rfc7638#autoid-13

Signed-off-by: Jose D. Gomez R <jose.gomez@suse.com>
This commit is contained in:
Jose D. Gomez R
2024-09-24 16:13:59 +02:00
committed by Jose D. Gomez R
parent b74618692d
commit b53946ded3
6 changed files with 177 additions and 14 deletions

View File

@@ -1,5 +1,15 @@
package token
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"fmt"
"math/big"
)
// actionSet is a special type of stringSet.
type actionSet struct {
stringSet
@@ -36,3 +46,41 @@ func containsAny(ss []string, q []string) bool {
return false
}
// NOTE: RFC7638 does not prescribe which hashing function to use, but suggests
// sha256 as a sane default as of time of writing
func hashAndEncode(payload string) string {
shasum := sha256.Sum256([]byte(payload))
return base64.RawURLEncoding.EncodeToString(shasum[:])
}
// RFC7638 states in section 3 sub 1 that the keys in the JSON object payload
// are required to be ordered lexicographical order. Golang does not guarantee
// order of keys[0]
// [0]: https://groups.google.com/g/golang-dev/c/zBQwhm3VfvU
//
// The payloads are small enough to create the JSON strings manually
func GetRFC7638Thumbprint(publickey crypto.PublicKey) string {
var payload string
switch pubkey := publickey.(type) {
case *rsa.PublicKey:
e_big := big.NewInt(int64(pubkey.E)).Bytes()
e := base64.RawURLEncoding.EncodeToString(e_big)
n := base64.RawURLEncoding.EncodeToString(pubkey.N.Bytes())
payload = fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, e, n)
case *ecdsa.PublicKey:
params := pubkey.Params()
crv := params.Name
x := base64.RawURLEncoding.EncodeToString(params.Gx.Bytes())
y := base64.RawURLEncoding.EncodeToString(params.Gy.Bytes())
payload = fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, crv, x, y)
default:
return ""
}
return hashAndEncode(payload)
}