xref: /aosp_15_r20/external/tink/go/jwt/jwk_converter.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1*e7b1675dSTing-Kang Chang// Copyright 2022 Google LLC
2*e7b1675dSTing-Kang Chang//
3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License");
4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License.
5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at
6*e7b1675dSTing-Kang Chang//
7*e7b1675dSTing-Kang Chang//      http://www.apache.org/licenses/LICENSE-2.0
8*e7b1675dSTing-Kang Chang//
9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software
10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS,
11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and
13*e7b1675dSTing-Kang Chang// limitations under the License.
14*e7b1675dSTing-Kang Chang//
15*e7b1675dSTing-Kang Chang////////////////////////////////////////////////////////////////////////////////
16*e7b1675dSTing-Kang Chang
17*e7b1675dSTing-Kang Changpackage jwt
18*e7b1675dSTing-Kang Chang
19*e7b1675dSTing-Kang Changimport (
20*e7b1675dSTing-Kang Chang	"bytes"
21*e7b1675dSTing-Kang Chang	"fmt"
22*e7b1675dSTing-Kang Chang	"math/rand"
23*e7b1675dSTing-Kang Chang
24*e7b1675dSTing-Kang Chang	spb "google.golang.org/protobuf/types/known/structpb"
25*e7b1675dSTing-Kang Chang	"google.golang.org/protobuf/proto"
26*e7b1675dSTing-Kang Chang	"github.com/google/tink/go/keyset"
27*e7b1675dSTing-Kang Chang	jepb "github.com/google/tink/go/proto/jwt_ecdsa_go_proto"
28*e7b1675dSTing-Kang Chang	jrsppb "github.com/google/tink/go/proto/jwt_rsa_ssa_pkcs1_go_proto"
29*e7b1675dSTing-Kang Chang	jrpsspb "github.com/google/tink/go/proto/jwt_rsa_ssa_pss_go_proto"
30*e7b1675dSTing-Kang Chang	tinkpb "github.com/google/tink/go/proto/tink_go_proto"
31*e7b1675dSTing-Kang Chang)
32*e7b1675dSTing-Kang Chang
33*e7b1675dSTing-Kang Changconst (
34*e7b1675dSTing-Kang Chang	jwtECDSAPublicKeyType = "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey"
35*e7b1675dSTing-Kang Chang	jwtRSPublicKeyType    = "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey"
36*e7b1675dSTing-Kang Chang	jwtPSPublicKeyType    = "type.googleapis.com/google.crypto.tink.JwtRsaSsaPssPublicKey"
37*e7b1675dSTing-Kang Chang)
38*e7b1675dSTing-Kang Chang
39*e7b1675dSTing-Kang Changfunc keysetHasID(ks *tinkpb.Keyset, keyID uint32) bool {
40*e7b1675dSTing-Kang Chang	for _, k := range ks.GetKey() {
41*e7b1675dSTing-Kang Chang		if k.GetKeyId() == keyID {
42*e7b1675dSTing-Kang Chang			return true
43*e7b1675dSTing-Kang Chang		}
44*e7b1675dSTing-Kang Chang	}
45*e7b1675dSTing-Kang Chang	return false
46*e7b1675dSTing-Kang Chang}
47*e7b1675dSTing-Kang Chang
48*e7b1675dSTing-Kang Changfunc generateUnusedID(ks *tinkpb.Keyset) uint32 {
49*e7b1675dSTing-Kang Chang	for {
50*e7b1675dSTing-Kang Chang		keyID := rand.Uint32()
51*e7b1675dSTing-Kang Chang		if !keysetHasID(ks, keyID) {
52*e7b1675dSTing-Kang Chang			return keyID
53*e7b1675dSTing-Kang Chang		}
54*e7b1675dSTing-Kang Chang	}
55*e7b1675dSTing-Kang Chang}
56*e7b1675dSTing-Kang Chang
57*e7b1675dSTing-Kang Changfunc hasItem(s *spb.Struct, name string) bool {
58*e7b1675dSTing-Kang Chang	if s.GetFields() == nil {
59*e7b1675dSTing-Kang Chang		return false
60*e7b1675dSTing-Kang Chang	}
61*e7b1675dSTing-Kang Chang	_, ok := s.Fields[name]
62*e7b1675dSTing-Kang Chang	return ok
63*e7b1675dSTing-Kang Chang}
64*e7b1675dSTing-Kang Chang
65*e7b1675dSTing-Kang Changfunc stringItem(s *spb.Struct, name string) (string, error) {
66*e7b1675dSTing-Kang Chang	fields := s.GetFields()
67*e7b1675dSTing-Kang Chang	if fields == nil {
68*e7b1675dSTing-Kang Chang		return "", fmt.Errorf("no fields")
69*e7b1675dSTing-Kang Chang	}
70*e7b1675dSTing-Kang Chang	val, ok := fields[name]
71*e7b1675dSTing-Kang Chang	if !ok {
72*e7b1675dSTing-Kang Chang		return "", fmt.Errorf("field %q not found", name)
73*e7b1675dSTing-Kang Chang	}
74*e7b1675dSTing-Kang Chang	r, ok := val.Kind.(*spb.Value_StringValue)
75*e7b1675dSTing-Kang Chang	if !ok {
76*e7b1675dSTing-Kang Chang		return "", fmt.Errorf("field %q is not a string", name)
77*e7b1675dSTing-Kang Chang	}
78*e7b1675dSTing-Kang Chang	return r.StringValue, nil
79*e7b1675dSTing-Kang Chang}
80*e7b1675dSTing-Kang Chang
81*e7b1675dSTing-Kang Changfunc listValue(s *spb.Struct, name string) (*spb.ListValue, error) {
82*e7b1675dSTing-Kang Chang	fields := s.GetFields()
83*e7b1675dSTing-Kang Chang	if fields == nil {
84*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("empty set")
85*e7b1675dSTing-Kang Chang	}
86*e7b1675dSTing-Kang Chang	vals, ok := fields[name]
87*e7b1675dSTing-Kang Chang	if !ok {
88*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("%q not found", name)
89*e7b1675dSTing-Kang Chang	}
90*e7b1675dSTing-Kang Chang	list, ok := vals.Kind.(*spb.Value_ListValue)
91*e7b1675dSTing-Kang Chang	if !ok {
92*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("%q is not a list", name)
93*e7b1675dSTing-Kang Chang	}
94*e7b1675dSTing-Kang Chang	if list.ListValue == nil || len(list.ListValue.GetValues()) == 0 {
95*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("%q list is empty", name)
96*e7b1675dSTing-Kang Chang	}
97*e7b1675dSTing-Kang Chang	return list.ListValue, nil
98*e7b1675dSTing-Kang Chang}
99*e7b1675dSTing-Kang Chang
100*e7b1675dSTing-Kang Changfunc expectStringItem(s *spb.Struct, name, value string) error {
101*e7b1675dSTing-Kang Chang	item, err := stringItem(s, name)
102*e7b1675dSTing-Kang Chang	if err != nil {
103*e7b1675dSTing-Kang Chang		return err
104*e7b1675dSTing-Kang Chang	}
105*e7b1675dSTing-Kang Chang	if item != value {
106*e7b1675dSTing-Kang Chang		return fmt.Errorf("unexpected value %q for %q", value, name)
107*e7b1675dSTing-Kang Chang	}
108*e7b1675dSTing-Kang Chang	return nil
109*e7b1675dSTing-Kang Chang}
110*e7b1675dSTing-Kang Chang
111*e7b1675dSTing-Kang Changfunc decodeItem(s *spb.Struct, name string) ([]byte, error) {
112*e7b1675dSTing-Kang Chang	e, err := stringItem(s, name)
113*e7b1675dSTing-Kang Chang	if err != nil {
114*e7b1675dSTing-Kang Chang		return nil, err
115*e7b1675dSTing-Kang Chang	}
116*e7b1675dSTing-Kang Chang	return base64Decode(e)
117*e7b1675dSTing-Kang Chang}
118*e7b1675dSTing-Kang Chang
119*e7b1675dSTing-Kang Changfunc validateKeyOPSIsVerify(s *spb.Struct) error {
120*e7b1675dSTing-Kang Chang	if !hasItem(s, "key_ops") {
121*e7b1675dSTing-Kang Chang		return nil
122*e7b1675dSTing-Kang Chang	}
123*e7b1675dSTing-Kang Chang	keyOPSList, err := listValue(s, "key_ops")
124*e7b1675dSTing-Kang Chang	if err != nil {
125*e7b1675dSTing-Kang Chang		return err
126*e7b1675dSTing-Kang Chang	}
127*e7b1675dSTing-Kang Chang	if len(keyOPSList.GetValues()) != 1 {
128*e7b1675dSTing-Kang Chang		return fmt.Errorf("key_ops size is not 1")
129*e7b1675dSTing-Kang Chang	}
130*e7b1675dSTing-Kang Chang	value, ok := keyOPSList.GetValues()[0].Kind.(*spb.Value_StringValue)
131*e7b1675dSTing-Kang Chang	if !ok {
132*e7b1675dSTing-Kang Chang		return fmt.Errorf("key_ops is not a string")
133*e7b1675dSTing-Kang Chang	}
134*e7b1675dSTing-Kang Chang	if value.StringValue != "verify" {
135*e7b1675dSTing-Kang Chang		return fmt.Errorf("key_ops is not equal to [\"verify\"]")
136*e7b1675dSTing-Kang Chang	}
137*e7b1675dSTing-Kang Chang	return nil
138*e7b1675dSTing-Kang Chang}
139*e7b1675dSTing-Kang Chang
140*e7b1675dSTing-Kang Changfunc validateUseIsSig(s *spb.Struct) error {
141*e7b1675dSTing-Kang Chang	if !hasItem(s, "use") {
142*e7b1675dSTing-Kang Chang		return nil
143*e7b1675dSTing-Kang Chang	}
144*e7b1675dSTing-Kang Chang	return expectStringItem(s, "use", "sig")
145*e7b1675dSTing-Kang Chang}
146*e7b1675dSTing-Kang Chang
147*e7b1675dSTing-Kang Changfunc algorithmPrefix(s *spb.Struct) (string, error) {
148*e7b1675dSTing-Kang Chang	alg, err := stringItem(s, "alg")
149*e7b1675dSTing-Kang Chang	if err != nil {
150*e7b1675dSTing-Kang Chang		return "", err
151*e7b1675dSTing-Kang Chang	}
152*e7b1675dSTing-Kang Chang	if len(alg) < 2 {
153*e7b1675dSTing-Kang Chang		return "", fmt.Errorf("invalid algorithm")
154*e7b1675dSTing-Kang Chang	}
155*e7b1675dSTing-Kang Chang	return alg[0:2], nil
156*e7b1675dSTing-Kang Chang}
157*e7b1675dSTing-Kang Chang
158*e7b1675dSTing-Kang Changvar psNameToAlg = map[string]jrpsspb.JwtRsaSsaPssAlgorithm{
159*e7b1675dSTing-Kang Chang	"PS256": jrpsspb.JwtRsaSsaPssAlgorithm_PS256,
160*e7b1675dSTing-Kang Chang	"PS384": jrpsspb.JwtRsaSsaPssAlgorithm_PS384,
161*e7b1675dSTing-Kang Chang	"PS512": jrpsspb.JwtRsaSsaPssAlgorithm_PS512,
162*e7b1675dSTing-Kang Chang}
163*e7b1675dSTing-Kang Chang
164*e7b1675dSTing-Kang Changfunc psPublicKeyDataFromStruct(keyStruct *spb.Struct) (*tinkpb.KeyData, error) {
165*e7b1675dSTing-Kang Chang	alg, err := stringItem(keyStruct, "alg")
166*e7b1675dSTing-Kang Chang	if err != nil {
167*e7b1675dSTing-Kang Chang		return nil, err
168*e7b1675dSTing-Kang Chang	}
169*e7b1675dSTing-Kang Chang	algorithm, ok := psNameToAlg[alg]
170*e7b1675dSTing-Kang Chang	if !ok {
171*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("invalid alg header: %q", alg)
172*e7b1675dSTing-Kang Chang	}
173*e7b1675dSTing-Kang Chang	rsaPubKey, err := rsaPubKeyFromStruct(keyStruct)
174*e7b1675dSTing-Kang Chang	if err != nil {
175*e7b1675dSTing-Kang Chang		return nil, err
176*e7b1675dSTing-Kang Chang	}
177*e7b1675dSTing-Kang Chang	jwtPubKey := &jrpsspb.JwtRsaSsaPssPublicKey{
178*e7b1675dSTing-Kang Chang		Version:   jwtECDSASignerKeyVersion,
179*e7b1675dSTing-Kang Chang		Algorithm: algorithm,
180*e7b1675dSTing-Kang Chang		E:         rsaPubKey.exponent,
181*e7b1675dSTing-Kang Chang		N:         rsaPubKey.modulus,
182*e7b1675dSTing-Kang Chang	}
183*e7b1675dSTing-Kang Chang	if rsaPubKey.customKID != nil {
184*e7b1675dSTing-Kang Chang		jwtPubKey.CustomKid = &jrpsspb.JwtRsaSsaPssPublicKey_CustomKid{
185*e7b1675dSTing-Kang Chang			Value: *rsaPubKey.customKID,
186*e7b1675dSTing-Kang Chang		}
187*e7b1675dSTing-Kang Chang	}
188*e7b1675dSTing-Kang Chang	serializedPubKey, err := proto.Marshal(jwtPubKey)
189*e7b1675dSTing-Kang Chang	if err != nil {
190*e7b1675dSTing-Kang Chang		return nil, err
191*e7b1675dSTing-Kang Chang	}
192*e7b1675dSTing-Kang Chang	return &tinkpb.KeyData{
193*e7b1675dSTing-Kang Chang		TypeUrl:         jwtPSPublicKeyType,
194*e7b1675dSTing-Kang Chang		Value:           serializedPubKey,
195*e7b1675dSTing-Kang Chang		KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC,
196*e7b1675dSTing-Kang Chang	}, nil
197*e7b1675dSTing-Kang Chang}
198*e7b1675dSTing-Kang Chang
199*e7b1675dSTing-Kang Changvar rsNameToAlg = map[string]jrsppb.JwtRsaSsaPkcs1Algorithm{
200*e7b1675dSTing-Kang Chang	"RS256": jrsppb.JwtRsaSsaPkcs1Algorithm_RS256,
201*e7b1675dSTing-Kang Chang	"RS384": jrsppb.JwtRsaSsaPkcs1Algorithm_RS384,
202*e7b1675dSTing-Kang Chang	"RS512": jrsppb.JwtRsaSsaPkcs1Algorithm_RS512,
203*e7b1675dSTing-Kang Chang}
204*e7b1675dSTing-Kang Chang
205*e7b1675dSTing-Kang Changfunc rsPublicKeyDataFromStruct(keyStruct *spb.Struct) (*tinkpb.KeyData, error) {
206*e7b1675dSTing-Kang Chang	alg, err := stringItem(keyStruct, "alg")
207*e7b1675dSTing-Kang Chang	if err != nil {
208*e7b1675dSTing-Kang Chang		return nil, err
209*e7b1675dSTing-Kang Chang	}
210*e7b1675dSTing-Kang Chang	algorithm, ok := rsNameToAlg[alg]
211*e7b1675dSTing-Kang Chang	if !ok {
212*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("invalid alg header: %q", alg)
213*e7b1675dSTing-Kang Chang	}
214*e7b1675dSTing-Kang Chang	rsaPubKey, err := rsaPubKeyFromStruct(keyStruct)
215*e7b1675dSTing-Kang Chang	if err != nil {
216*e7b1675dSTing-Kang Chang		return nil, err
217*e7b1675dSTing-Kang Chang	}
218*e7b1675dSTing-Kang Chang	jwtPubKey := &jrsppb.JwtRsaSsaPkcs1PublicKey{
219*e7b1675dSTing-Kang Chang		Version:   0,
220*e7b1675dSTing-Kang Chang		Algorithm: algorithm,
221*e7b1675dSTing-Kang Chang		E:         rsaPubKey.exponent,
222*e7b1675dSTing-Kang Chang		N:         rsaPubKey.modulus,
223*e7b1675dSTing-Kang Chang	}
224*e7b1675dSTing-Kang Chang	if rsaPubKey.customKID != nil {
225*e7b1675dSTing-Kang Chang		jwtPubKey.CustomKid = &jrsppb.JwtRsaSsaPkcs1PublicKey_CustomKid{
226*e7b1675dSTing-Kang Chang			Value: *rsaPubKey.customKID,
227*e7b1675dSTing-Kang Chang		}
228*e7b1675dSTing-Kang Chang	}
229*e7b1675dSTing-Kang Chang	serializedPubKey, err := proto.Marshal(jwtPubKey)
230*e7b1675dSTing-Kang Chang	if err != nil {
231*e7b1675dSTing-Kang Chang		return nil, err
232*e7b1675dSTing-Kang Chang	}
233*e7b1675dSTing-Kang Chang	return &tinkpb.KeyData{
234*e7b1675dSTing-Kang Chang		TypeUrl:         jwtRSPublicKeyType,
235*e7b1675dSTing-Kang Chang		Value:           serializedPubKey,
236*e7b1675dSTing-Kang Chang		KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC,
237*e7b1675dSTing-Kang Chang	}, nil
238*e7b1675dSTing-Kang Chang}
239*e7b1675dSTing-Kang Chang
240*e7b1675dSTing-Kang Changtype rsaPubKey struct {
241*e7b1675dSTing-Kang Chang	exponent  []byte
242*e7b1675dSTing-Kang Chang	modulus   []byte
243*e7b1675dSTing-Kang Chang	customKID *string
244*e7b1675dSTing-Kang Chang}
245*e7b1675dSTing-Kang Chang
246*e7b1675dSTing-Kang Changfunc rsaPubKeyFromStruct(keyStruct *spb.Struct) (*rsaPubKey, error) {
247*e7b1675dSTing-Kang Chang	if hasItem(keyStruct, "p") ||
248*e7b1675dSTing-Kang Chang		hasItem(keyStruct, "q") ||
249*e7b1675dSTing-Kang Chang		hasItem(keyStruct, "dq") ||
250*e7b1675dSTing-Kang Chang		hasItem(keyStruct, "dp") ||
251*e7b1675dSTing-Kang Chang		hasItem(keyStruct, "d") ||
252*e7b1675dSTing-Kang Chang		hasItem(keyStruct, "qi") {
253*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("private key can't be converted")
254*e7b1675dSTing-Kang Chang	}
255*e7b1675dSTing-Kang Chang	if err := expectStringItem(keyStruct, "kty", "RSA"); err != nil {
256*e7b1675dSTing-Kang Chang		return nil, err
257*e7b1675dSTing-Kang Chang	}
258*e7b1675dSTing-Kang Chang	if err := validateUseIsSig(keyStruct); err != nil {
259*e7b1675dSTing-Kang Chang		return nil, err
260*e7b1675dSTing-Kang Chang	}
261*e7b1675dSTing-Kang Chang	if err := validateKeyOPSIsVerify(keyStruct); err != nil {
262*e7b1675dSTing-Kang Chang		return nil, err
263*e7b1675dSTing-Kang Chang	}
264*e7b1675dSTing-Kang Chang	e, err := decodeItem(keyStruct, "e")
265*e7b1675dSTing-Kang Chang	if err != nil {
266*e7b1675dSTing-Kang Chang		return nil, err
267*e7b1675dSTing-Kang Chang	}
268*e7b1675dSTing-Kang Chang	n, err := decodeItem(keyStruct, "n")
269*e7b1675dSTing-Kang Chang	if err != nil {
270*e7b1675dSTing-Kang Chang		return nil, err
271*e7b1675dSTing-Kang Chang	}
272*e7b1675dSTing-Kang Chang	var customKID *string = nil
273*e7b1675dSTing-Kang Chang	if hasItem(keyStruct, "kid") {
274*e7b1675dSTing-Kang Chang		kid, err := stringItem(keyStruct, "kid")
275*e7b1675dSTing-Kang Chang		if err != nil {
276*e7b1675dSTing-Kang Chang			return nil, err
277*e7b1675dSTing-Kang Chang		}
278*e7b1675dSTing-Kang Chang		customKID = &kid
279*e7b1675dSTing-Kang Chang	}
280*e7b1675dSTing-Kang Chang	return &rsaPubKey{
281*e7b1675dSTing-Kang Chang		exponent:  e,
282*e7b1675dSTing-Kang Chang		modulus:   n,
283*e7b1675dSTing-Kang Chang		customKID: customKID,
284*e7b1675dSTing-Kang Chang	}, nil
285*e7b1675dSTing-Kang Chang}
286*e7b1675dSTing-Kang Chang
287*e7b1675dSTing-Kang Changfunc esPublicKeyDataFromStruct(keyStruct *spb.Struct) (*tinkpb.KeyData, error) {
288*e7b1675dSTing-Kang Chang	alg, err := stringItem(keyStruct, "alg")
289*e7b1675dSTing-Kang Chang	if err != nil {
290*e7b1675dSTing-Kang Chang		return nil, err
291*e7b1675dSTing-Kang Chang	}
292*e7b1675dSTing-Kang Chang	curve, err := stringItem(keyStruct, "crv")
293*e7b1675dSTing-Kang Chang	if err != nil {
294*e7b1675dSTing-Kang Chang		return nil, err
295*e7b1675dSTing-Kang Chang	}
296*e7b1675dSTing-Kang Chang	var algorithm jepb.JwtEcdsaAlgorithm = jepb.JwtEcdsaAlgorithm_ES_UNKNOWN
297*e7b1675dSTing-Kang Chang	if alg == "ES256" && curve == "P-256" {
298*e7b1675dSTing-Kang Chang		algorithm = jepb.JwtEcdsaAlgorithm_ES256
299*e7b1675dSTing-Kang Chang	}
300*e7b1675dSTing-Kang Chang	if alg == "ES384" && curve == "P-384" {
301*e7b1675dSTing-Kang Chang		algorithm = jepb.JwtEcdsaAlgorithm_ES384
302*e7b1675dSTing-Kang Chang	}
303*e7b1675dSTing-Kang Chang	if alg == "ES512" && curve == "P-521" {
304*e7b1675dSTing-Kang Chang		algorithm = jepb.JwtEcdsaAlgorithm_ES512
305*e7b1675dSTing-Kang Chang	}
306*e7b1675dSTing-Kang Chang	if algorithm == jepb.JwtEcdsaAlgorithm_ES_UNKNOWN {
307*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("invalid algorithm %q and curve %q", alg, curve)
308*e7b1675dSTing-Kang Chang	}
309*e7b1675dSTing-Kang Chang	if hasItem(keyStruct, "d") {
310*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("private keys cannot be converted")
311*e7b1675dSTing-Kang Chang	}
312*e7b1675dSTing-Kang Chang	if err := expectStringItem(keyStruct, "kty", "EC"); err != nil {
313*e7b1675dSTing-Kang Chang		return nil, err
314*e7b1675dSTing-Kang Chang	}
315*e7b1675dSTing-Kang Chang	if err := validateUseIsSig(keyStruct); err != nil {
316*e7b1675dSTing-Kang Chang		return nil, err
317*e7b1675dSTing-Kang Chang	}
318*e7b1675dSTing-Kang Chang	if err := validateKeyOPSIsVerify(keyStruct); err != nil {
319*e7b1675dSTing-Kang Chang		return nil, err
320*e7b1675dSTing-Kang Chang	}
321*e7b1675dSTing-Kang Chang	x, err := decodeItem(keyStruct, "x")
322*e7b1675dSTing-Kang Chang	if err != nil {
323*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("failed to decode x: %v", err)
324*e7b1675dSTing-Kang Chang	}
325*e7b1675dSTing-Kang Chang	y, err := decodeItem(keyStruct, "y")
326*e7b1675dSTing-Kang Chang	if err != nil {
327*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("failed to decode y: %v", err)
328*e7b1675dSTing-Kang Chang	}
329*e7b1675dSTing-Kang Chang	var customKID *jepb.JwtEcdsaPublicKey_CustomKid = nil
330*e7b1675dSTing-Kang Chang	if hasItem(keyStruct, "kid") {
331*e7b1675dSTing-Kang Chang		kid, err := stringItem(keyStruct, "kid")
332*e7b1675dSTing-Kang Chang		if err != nil {
333*e7b1675dSTing-Kang Chang			return nil, err
334*e7b1675dSTing-Kang Chang		}
335*e7b1675dSTing-Kang Chang		customKID = &jepb.JwtEcdsaPublicKey_CustomKid{Value: kid}
336*e7b1675dSTing-Kang Chang	}
337*e7b1675dSTing-Kang Chang	pubKey := &jepb.JwtEcdsaPublicKey{
338*e7b1675dSTing-Kang Chang		Version:   0,
339*e7b1675dSTing-Kang Chang		Algorithm: algorithm,
340*e7b1675dSTing-Kang Chang		X:         x,
341*e7b1675dSTing-Kang Chang		Y:         y,
342*e7b1675dSTing-Kang Chang		CustomKid: customKID,
343*e7b1675dSTing-Kang Chang	}
344*e7b1675dSTing-Kang Chang	serializedPubKey, err := proto.Marshal(pubKey)
345*e7b1675dSTing-Kang Chang	if err != nil {
346*e7b1675dSTing-Kang Chang		return nil, err
347*e7b1675dSTing-Kang Chang	}
348*e7b1675dSTing-Kang Chang	return &tinkpb.KeyData{
349*e7b1675dSTing-Kang Chang		TypeUrl:         jwtECDSAPublicKeyType,
350*e7b1675dSTing-Kang Chang		Value:           serializedPubKey,
351*e7b1675dSTing-Kang Chang		KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC,
352*e7b1675dSTing-Kang Chang	}, nil
353*e7b1675dSTing-Kang Chang}
354*e7b1675dSTing-Kang Chang
355*e7b1675dSTing-Kang Changfunc keysetKeyFromStruct(val *spb.Value, keyID uint32) (*tinkpb.Keyset_Key, error) {
356*e7b1675dSTing-Kang Chang	keyStruct := val.GetStructValue()
357*e7b1675dSTing-Kang Chang	if keyStruct == nil {
358*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("key is not a JSON object")
359*e7b1675dSTing-Kang Chang	}
360*e7b1675dSTing-Kang Chang	algPrefix, err := algorithmPrefix(keyStruct)
361*e7b1675dSTing-Kang Chang	if err != nil {
362*e7b1675dSTing-Kang Chang		return nil, err
363*e7b1675dSTing-Kang Chang	}
364*e7b1675dSTing-Kang Chang	var keyData *tinkpb.KeyData
365*e7b1675dSTing-Kang Chang	switch algPrefix {
366*e7b1675dSTing-Kang Chang	case "ES":
367*e7b1675dSTing-Kang Chang		keyData, err = esPublicKeyDataFromStruct(keyStruct)
368*e7b1675dSTing-Kang Chang	case "RS":
369*e7b1675dSTing-Kang Chang		keyData, err = rsPublicKeyDataFromStruct(keyStruct)
370*e7b1675dSTing-Kang Chang	case "PS":
371*e7b1675dSTing-Kang Chang		keyData, err = psPublicKeyDataFromStruct(keyStruct)
372*e7b1675dSTing-Kang Chang	default:
373*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("unsupported algorithm prefix: %v", algPrefix)
374*e7b1675dSTing-Kang Chang	}
375*e7b1675dSTing-Kang Chang	if err != nil {
376*e7b1675dSTing-Kang Chang		return nil, err
377*e7b1675dSTing-Kang Chang	}
378*e7b1675dSTing-Kang Chang	return &tinkpb.Keyset_Key{
379*e7b1675dSTing-Kang Chang		KeyData:          keyData,
380*e7b1675dSTing-Kang Chang		Status:           tinkpb.KeyStatusType_ENABLED,
381*e7b1675dSTing-Kang Chang		OutputPrefixType: tinkpb.OutputPrefixType_RAW,
382*e7b1675dSTing-Kang Chang		KeyId:            keyID,
383*e7b1675dSTing-Kang Chang	}, nil
384*e7b1675dSTing-Kang Chang}
385*e7b1675dSTing-Kang Chang
386*e7b1675dSTing-Kang Chang// JWKSetToPublicKeysetHandle converts a Json Web Key (JWK) set into a Tink KeysetHandle.
387*e7b1675dSTing-Kang Chang// It requires that all keys in the set have the "alg" field set. Currently, only
388*e7b1675dSTing-Kang Chang// public keys for algorithms ES256, ES384, ES512, RS256, RS384, and RS512 are supported.
389*e7b1675dSTing-Kang Chang// JWK is defined in https://www.rfc-editor.org/rfc/rfc7517.txt.
390*e7b1675dSTing-Kang Changfunc JWKSetToPublicKeysetHandle(jwkSet []byte) (*keyset.Handle, error) {
391*e7b1675dSTing-Kang Chang	jwk := &spb.Struct{}
392*e7b1675dSTing-Kang Chang	if err := jwk.UnmarshalJSON(jwkSet); err != nil {
393*e7b1675dSTing-Kang Chang		return nil, err
394*e7b1675dSTing-Kang Chang	}
395*e7b1675dSTing-Kang Chang	keyList, err := listValue(jwk, "keys")
396*e7b1675dSTing-Kang Chang	if err != nil {
397*e7b1675dSTing-Kang Chang		return nil, err
398*e7b1675dSTing-Kang Chang	}
399*e7b1675dSTing-Kang Chang
400*e7b1675dSTing-Kang Chang	ks := &tinkpb.Keyset{}
401*e7b1675dSTing-Kang Chang	for _, keyStruct := range keyList.GetValues() {
402*e7b1675dSTing-Kang Chang		key, err := keysetKeyFromStruct(keyStruct, generateUnusedID(ks))
403*e7b1675dSTing-Kang Chang		if err != nil {
404*e7b1675dSTing-Kang Chang			return nil, err
405*e7b1675dSTing-Kang Chang		}
406*e7b1675dSTing-Kang Chang		ks.Key = append(ks.Key, key)
407*e7b1675dSTing-Kang Chang	}
408*e7b1675dSTing-Kang Chang	ks.PrimaryKeyId = ks.Key[len(ks.Key)-1].GetKeyId()
409*e7b1675dSTing-Kang Chang	return keyset.NewHandleWithNoSecrets(ks)
410*e7b1675dSTing-Kang Chang}
411*e7b1675dSTing-Kang Chang
412*e7b1675dSTing-Kang Changfunc addKeyOPSVerify(s *spb.Struct) {
413*e7b1675dSTing-Kang Chang	s.GetFields()["key_ops"] = spb.NewListValue(&spb.ListValue{Values: []*spb.Value{spb.NewStringValue("verify")}})
414*e7b1675dSTing-Kang Chang}
415*e7b1675dSTing-Kang Chang
416*e7b1675dSTing-Kang Changfunc addStringEntry(s *spb.Struct, key, val string) {
417*e7b1675dSTing-Kang Chang	s.GetFields()[key] = spb.NewStringValue(val)
418*e7b1675dSTing-Kang Chang}
419*e7b1675dSTing-Kang Chang
420*e7b1675dSTing-Kang Changvar psAlgToStr map[jrpsspb.JwtRsaSsaPssAlgorithm]string = map[jrpsspb.JwtRsaSsaPssAlgorithm]string{
421*e7b1675dSTing-Kang Chang	jrpsspb.JwtRsaSsaPssAlgorithm_PS256: "PS256",
422*e7b1675dSTing-Kang Chang	jrpsspb.JwtRsaSsaPssAlgorithm_PS384: "PS384",
423*e7b1675dSTing-Kang Chang	jrpsspb.JwtRsaSsaPssAlgorithm_PS512: "PS512",
424*e7b1675dSTing-Kang Chang}
425*e7b1675dSTing-Kang Chang
426*e7b1675dSTing-Kang Changfunc psPublicKeyToStruct(key *tinkpb.Keyset_Key) (*spb.Struct, error) {
427*e7b1675dSTing-Kang Chang	pubKey := &jrpsspb.JwtRsaSsaPssPublicKey{}
428*e7b1675dSTing-Kang Chang	if err := proto.Unmarshal(key.GetKeyData().GetValue(), pubKey); err != nil {
429*e7b1675dSTing-Kang Chang		return nil, err
430*e7b1675dSTing-Kang Chang	}
431*e7b1675dSTing-Kang Chang	alg, ok := psAlgToStr[pubKey.GetAlgorithm()]
432*e7b1675dSTing-Kang Chang	if !ok {
433*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("invalid algorithm")
434*e7b1675dSTing-Kang Chang	}
435*e7b1675dSTing-Kang Chang	outKey := &spb.Struct{
436*e7b1675dSTing-Kang Chang		Fields: map[string]*spb.Value{},
437*e7b1675dSTing-Kang Chang	}
438*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "alg", alg)
439*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "kty", "RSA")
440*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "e", base64Encode(pubKey.GetE()))
441*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "n", base64Encode(pubKey.GetN()))
442*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "use", "sig")
443*e7b1675dSTing-Kang Chang	addKeyOPSVerify(outKey)
444*e7b1675dSTing-Kang Chang	var customKID *string = nil
445*e7b1675dSTing-Kang Chang	if pubKey.GetCustomKid() != nil {
446*e7b1675dSTing-Kang Chang		ck := pubKey.GetCustomKid().GetValue()
447*e7b1675dSTing-Kang Chang		customKID = &ck
448*e7b1675dSTing-Kang Chang	}
449*e7b1675dSTing-Kang Chang	if err := setKeyID(outKey, key, customKID); err != nil {
450*e7b1675dSTing-Kang Chang		return nil, err
451*e7b1675dSTing-Kang Chang	}
452*e7b1675dSTing-Kang Chang	return outKey, nil
453*e7b1675dSTing-Kang Chang}
454*e7b1675dSTing-Kang Chang
455*e7b1675dSTing-Kang Changvar rsAlgToStr map[jrsppb.JwtRsaSsaPkcs1Algorithm]string = map[jrsppb.JwtRsaSsaPkcs1Algorithm]string{
456*e7b1675dSTing-Kang Chang	jrsppb.JwtRsaSsaPkcs1Algorithm_RS256: "RS256",
457*e7b1675dSTing-Kang Chang	jrsppb.JwtRsaSsaPkcs1Algorithm_RS384: "RS384",
458*e7b1675dSTing-Kang Chang	jrsppb.JwtRsaSsaPkcs1Algorithm_RS512: "RS512",
459*e7b1675dSTing-Kang Chang}
460*e7b1675dSTing-Kang Chang
461*e7b1675dSTing-Kang Changfunc rsPublicKeyToStruct(key *tinkpb.Keyset_Key) (*spb.Struct, error) {
462*e7b1675dSTing-Kang Chang	pubKey := &jrsppb.JwtRsaSsaPkcs1PublicKey{}
463*e7b1675dSTing-Kang Chang	if err := proto.Unmarshal(key.GetKeyData().GetValue(), pubKey); err != nil {
464*e7b1675dSTing-Kang Chang		return nil, err
465*e7b1675dSTing-Kang Chang	}
466*e7b1675dSTing-Kang Chang	alg, ok := rsAlgToStr[pubKey.GetAlgorithm()]
467*e7b1675dSTing-Kang Chang	if !ok {
468*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("invalid algorithm")
469*e7b1675dSTing-Kang Chang	}
470*e7b1675dSTing-Kang Chang	outKey := &spb.Struct{
471*e7b1675dSTing-Kang Chang		Fields: map[string]*spb.Value{},
472*e7b1675dSTing-Kang Chang	}
473*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "alg", alg)
474*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "kty", "RSA")
475*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "e", base64Encode(pubKey.GetE()))
476*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "n", base64Encode(pubKey.GetN()))
477*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "use", "sig")
478*e7b1675dSTing-Kang Chang	addKeyOPSVerify(outKey)
479*e7b1675dSTing-Kang Chang
480*e7b1675dSTing-Kang Chang	var customKID *string = nil
481*e7b1675dSTing-Kang Chang	if pubKey.GetCustomKid() != nil {
482*e7b1675dSTing-Kang Chang		ck := pubKey.GetCustomKid().GetValue()
483*e7b1675dSTing-Kang Chang		customKID = &ck
484*e7b1675dSTing-Kang Chang	}
485*e7b1675dSTing-Kang Chang	if err := setKeyID(outKey, key, customKID); err != nil {
486*e7b1675dSTing-Kang Chang		return nil, err
487*e7b1675dSTing-Kang Chang	}
488*e7b1675dSTing-Kang Chang	return outKey, nil
489*e7b1675dSTing-Kang Chang}
490*e7b1675dSTing-Kang Chang
491*e7b1675dSTing-Kang Changfunc esPublicKeyToStruct(key *tinkpb.Keyset_Key) (*spb.Struct, error) {
492*e7b1675dSTing-Kang Chang	pubKey := &jepb.JwtEcdsaPublicKey{}
493*e7b1675dSTing-Kang Chang	if err := proto.Unmarshal(key.GetKeyData().GetValue(), pubKey); err != nil {
494*e7b1675dSTing-Kang Chang		return nil, err
495*e7b1675dSTing-Kang Chang	}
496*e7b1675dSTing-Kang Chang	outKey := &spb.Struct{
497*e7b1675dSTing-Kang Chang		Fields: map[string]*spb.Value{},
498*e7b1675dSTing-Kang Chang	}
499*e7b1675dSTing-Kang Chang	var algorithm, curve string
500*e7b1675dSTing-Kang Chang	switch pubKey.GetAlgorithm() {
501*e7b1675dSTing-Kang Chang	case jepb.JwtEcdsaAlgorithm_ES256:
502*e7b1675dSTing-Kang Chang		curve, algorithm = "P-256", "ES256"
503*e7b1675dSTing-Kang Chang	case jepb.JwtEcdsaAlgorithm_ES384:
504*e7b1675dSTing-Kang Chang		curve, algorithm = "P-384", "ES384"
505*e7b1675dSTing-Kang Chang	case jepb.JwtEcdsaAlgorithm_ES512:
506*e7b1675dSTing-Kang Chang		curve, algorithm = "P-521", "ES512"
507*e7b1675dSTing-Kang Chang	default:
508*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("invalid algorithm")
509*e7b1675dSTing-Kang Chang	}
510*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "crv", curve)
511*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "alg", algorithm)
512*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "kty", "EC")
513*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "x", base64Encode(pubKey.GetX()))
514*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "y", base64Encode(pubKey.GetY()))
515*e7b1675dSTing-Kang Chang	addStringEntry(outKey, "use", "sig")
516*e7b1675dSTing-Kang Chang	addKeyOPSVerify(outKey)
517*e7b1675dSTing-Kang Chang
518*e7b1675dSTing-Kang Chang	var customKID *string = nil
519*e7b1675dSTing-Kang Chang	if pubKey.GetCustomKid() != nil {
520*e7b1675dSTing-Kang Chang		ck := pubKey.GetCustomKid().GetValue()
521*e7b1675dSTing-Kang Chang		customKID = &ck
522*e7b1675dSTing-Kang Chang	}
523*e7b1675dSTing-Kang Chang	if err := setKeyID(outKey, key, customKID); err != nil {
524*e7b1675dSTing-Kang Chang		return nil, err
525*e7b1675dSTing-Kang Chang	}
526*e7b1675dSTing-Kang Chang	return outKey, nil
527*e7b1675dSTing-Kang Chang}
528*e7b1675dSTing-Kang Chang
529*e7b1675dSTing-Kang Changfunc setKeyID(outKey *spb.Struct, key *tinkpb.Keyset_Key, customKID *string) error {
530*e7b1675dSTing-Kang Chang	if key.GetOutputPrefixType() == tinkpb.OutputPrefixType_TINK {
531*e7b1675dSTing-Kang Chang		if customKID != nil {
532*e7b1675dSTing-Kang Chang			return fmt.Errorf("TINK keys shouldn't have custom KID")
533*e7b1675dSTing-Kang Chang		}
534*e7b1675dSTing-Kang Chang		kid := keyID(key.KeyId, key.GetOutputPrefixType())
535*e7b1675dSTing-Kang Chang		if kid == nil {
536*e7b1675dSTing-Kang Chang			return fmt.Errorf("tink KID shouldn't be nil")
537*e7b1675dSTing-Kang Chang		}
538*e7b1675dSTing-Kang Chang		addStringEntry(outKey, "kid", *kid)
539*e7b1675dSTing-Kang Chang	} else if customKID != nil {
540*e7b1675dSTing-Kang Chang		addStringEntry(outKey, "kid", *customKID)
541*e7b1675dSTing-Kang Chang	}
542*e7b1675dSTing-Kang Chang	return nil
543*e7b1675dSTing-Kang Chang}
544*e7b1675dSTing-Kang Chang
545*e7b1675dSTing-Kang Chang// JWKSetFromPublicKeysetHandle converts a Tink KeysetHandle with JWT keys into a Json Web Key (JWK) set.
546*e7b1675dSTing-Kang Chang// Currently only public keys for algorithms ES256, ES384, ES512, RS256, RS384, and RS512 are supported.
547*e7b1675dSTing-Kang Chang// JWK is defined in https://www.rfc-editor.org/rfc/rfc7517.html.
548*e7b1675dSTing-Kang Changfunc JWKSetFromPublicKeysetHandle(kh *keyset.Handle) ([]byte, error) {
549*e7b1675dSTing-Kang Chang	b := &bytes.Buffer{}
550*e7b1675dSTing-Kang Chang	if err := kh.WriteWithNoSecrets(keyset.NewBinaryWriter(b)); err != nil {
551*e7b1675dSTing-Kang Chang		return nil, err
552*e7b1675dSTing-Kang Chang	}
553*e7b1675dSTing-Kang Chang	ks := &tinkpb.Keyset{}
554*e7b1675dSTing-Kang Chang	if err := proto.Unmarshal(b.Bytes(), ks); err != nil {
555*e7b1675dSTing-Kang Chang		return nil, err
556*e7b1675dSTing-Kang Chang	}
557*e7b1675dSTing-Kang Chang	keyValList := []*spb.Value{}
558*e7b1675dSTing-Kang Chang	for _, k := range ks.Key {
559*e7b1675dSTing-Kang Chang		if k.GetStatus() != tinkpb.KeyStatusType_ENABLED {
560*e7b1675dSTing-Kang Chang			continue
561*e7b1675dSTing-Kang Chang		}
562*e7b1675dSTing-Kang Chang		if k.GetOutputPrefixType() != tinkpb.OutputPrefixType_TINK &&
563*e7b1675dSTing-Kang Chang			k.GetOutputPrefixType() != tinkpb.OutputPrefixType_RAW {
564*e7b1675dSTing-Kang Chang			return nil, fmt.Errorf("unsupported output prefix type")
565*e7b1675dSTing-Kang Chang		}
566*e7b1675dSTing-Kang Chang		keyData := k.GetKeyData()
567*e7b1675dSTing-Kang Chang		if keyData == nil {
568*e7b1675dSTing-Kang Chang			return nil, fmt.Errorf("invalid key data")
569*e7b1675dSTing-Kang Chang		}
570*e7b1675dSTing-Kang Chang		if keyData.GetKeyMaterialType() != tinkpb.KeyData_ASYMMETRIC_PUBLIC {
571*e7b1675dSTing-Kang Chang			return nil, fmt.Errorf("only asymmetric public keys are supported")
572*e7b1675dSTing-Kang Chang		}
573*e7b1675dSTing-Kang Chang		keyStruct := &spb.Struct{}
574*e7b1675dSTing-Kang Chang		var err error
575*e7b1675dSTing-Kang Chang		switch keyData.GetTypeUrl() {
576*e7b1675dSTing-Kang Chang		case jwtECDSAPublicKeyType:
577*e7b1675dSTing-Kang Chang			keyStruct, err = esPublicKeyToStruct(k)
578*e7b1675dSTing-Kang Chang		case jwtRSPublicKeyType:
579*e7b1675dSTing-Kang Chang			keyStruct, err = rsPublicKeyToStruct(k)
580*e7b1675dSTing-Kang Chang		case jwtPSPublicKeyType:
581*e7b1675dSTing-Kang Chang			keyStruct, err = psPublicKeyToStruct(k)
582*e7b1675dSTing-Kang Chang		default:
583*e7b1675dSTing-Kang Chang			return nil, fmt.Errorf("unsupported key type url")
584*e7b1675dSTing-Kang Chang		}
585*e7b1675dSTing-Kang Chang		if err != nil {
586*e7b1675dSTing-Kang Chang			return nil, err
587*e7b1675dSTing-Kang Chang		}
588*e7b1675dSTing-Kang Chang		keyValList = append(keyValList, spb.NewStructValue(keyStruct))
589*e7b1675dSTing-Kang Chang	}
590*e7b1675dSTing-Kang Chang	output := &spb.Struct{
591*e7b1675dSTing-Kang Chang		Fields: map[string]*spb.Value{
592*e7b1675dSTing-Kang Chang			"keys": spb.NewListValue(&spb.ListValue{Values: keyValList}),
593*e7b1675dSTing-Kang Chang		},
594*e7b1675dSTing-Kang Chang	}
595*e7b1675dSTing-Kang Chang	return output.MarshalJSON()
596*e7b1675dSTing-Kang Chang}
597