File: backend.go

package info (click to toggle)
golang-github-openpubkey-openpubkey 0.18.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 964 kB
  • sloc: makefile: 10
file content (156 lines) | stat: -rw-r--r-- 4,943 bytes parent folder | download
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright 2024 OpenPubkey
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package mocks

import (
	"context"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"encoding/json"
	"fmt"
	mathrand "math/rand"

	"github.com/lestrrat-go/jwx/v2/jwk"
	"github.com/openpubkey/openpubkey/discover"
	"github.com/openpubkey/openpubkey/oidc"
	"golang.org/x/exp/maps"
)

type MockProviderBackend struct {
	Issuer                string
	PublicKeyFinder       discover.PublicKeyFinder
	ProviderSigningKeySet map[string]crypto.Signer            // kid (keyId) -> signing key
	ProviderPublicKeySet  map[string]discover.PublicKeyRecord // kid (keyId) -> PublicKeyRecord
	IDTokensTemplate      *IDTokenTemplate
}

func NewMockProviderBackend(issuer string, alg string, numKeys int) (*MockProviderBackend, error) {

	var providerSigningKeySet map[string]crypto.Signer
	var providerPublicKeySet map[string]discover.PublicKeyRecord
	var err error
	if alg == "RS256" {
		if providerSigningKeySet, providerPublicKeySet, err = CreateRS256KeySet(issuer, numKeys); err != nil {
			return nil, err
		}
	} else if alg == "ES256" {
		if providerSigningKeySet, providerPublicKeySet, err = CreateES256KeySet(issuer, numKeys); err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("unsupported provider alg: %s", alg)
	}

	return &MockProviderBackend{
		Issuer: issuer,
		PublicKeyFinder: discover.PublicKeyFinder{
			JwksFunc: func(ctx context.Context, issuer string) ([]byte, error) {
				keySet := jwk.NewSet()
				for kid, record := range providerPublicKeySet {
					jwkKey, err := jwk.PublicKeyOf(record.PublicKey)
					if err != nil {
						return nil, err
					}
					if err := jwkKey.Set(jwk.AlgorithmKey, record.Alg); err != nil {
						return nil, err
					}
					if err := jwkKey.Set(jwk.KeyIDKey, kid); err != nil {
						return nil, err
					}

					// Put our jwk into a set
					if err := keySet.AddKey(jwkKey); err != nil {
						return nil, err
					}
				}
				return json.MarshalIndent(keySet, "", "  ")
			},
		},
		ProviderSigningKeySet: providerSigningKeySet,
		ProviderPublicKeySet:  providerPublicKeySet,
	}, nil
}

func (o *MockProviderBackend) GetPublicKeyFinder() *discover.PublicKeyFinder {
	return &o.PublicKeyFinder
}

func (o *MockProviderBackend) GetProviderPublicKeySet() map[string]discover.PublicKeyRecord {
	return o.ProviderPublicKeySet
}

func (o *MockProviderBackend) GetProviderSigningKeySet() map[string]crypto.Signer {
	return o.ProviderSigningKeySet
}

func (o *MockProviderBackend) SetIDTokenTemplate(template *IDTokenTemplate) {
	o.IDTokensTemplate = template
}

func (o *MockProviderBackend) RequestTokensOverrideFunc(cicHash string) (*oidc.Tokens, error) {
	o.IDTokensTemplate.AddCommit(cicHash)
	return o.IDTokensTemplate.IssueTokens()
}

func (o *MockProviderBackend) RandomSigningKey() (crypto.Signer, string, discover.PublicKeyRecord) {
	keyIDs := maps.Keys(o.GetProviderPublicKeySet())
	keyID := keyIDs[mathrand.Intn(len(keyIDs))]
	return o.GetProviderSigningKeySet()[keyID], keyID, o.GetProviderPublicKeySet()[keyID]
}

func CreateRS256KeySet(issuer string, numKeys int) (map[string]crypto.Signer, map[string]discover.PublicKeyRecord, error) {
	return CreateKeySet(issuer, "RS256", numKeys)
}

func CreateES256KeySet(issuer string, numKeys int) (map[string]crypto.Signer, map[string]discover.PublicKeyRecord, error) {
	return CreateKeySet(issuer, "ES256", numKeys)
}

func CreateKeySet(issuer string, alg string, numKeys int) (map[string]crypto.Signer, map[string]discover.PublicKeyRecord, error) {
	providerSigningKeySet := map[string]crypto.Signer{}
	providerPublicKeySet := map[string]discover.PublicKeyRecord{}

	for i := 0; i < numKeys; i++ {
		kid := fmt.Sprintf("kid-%d", i)

		var signingKey crypto.Signer
		var err error
		switch alg {
		case "ES256":
			if signingKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil {
				return nil, nil, err
			}
		case "RS256":
			if signingKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
				return nil, nil, err
			}
		default:
			return nil, nil, fmt.Errorf("unsupported alg: %s", alg)
		}

		providerSigningKeySet[string(kid)] = signingKey
		providerPublicKeySet[string(kid)] = discover.PublicKeyRecord{
			PublicKey: signingKey.Public(),
			Alg:       alg,
			Issuer:    issuer,
		}
	}
	return providerSigningKeySet, providerPublicKeySet, nil
}