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