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 subtle 18*e7b1675dSTing-Kang Chang 19*e7b1675dSTing-Kang Changimport ( 20*e7b1675dSTing-Kang Chang "bytes" 21*e7b1675dSTing-Kang Chang "errors" 22*e7b1675dSTing-Kang Chang "fmt" 23*e7b1675dSTing-Kang Chang 24*e7b1675dSTing-Kang Chang "google.golang.org/protobuf/proto" 25*e7b1675dSTing-Kang Chang "github.com/google/tink/go/keyset" 26*e7b1675dSTing-Kang Chang hpkepb "github.com/google/tink/go/proto/hpke_go_proto" 27*e7b1675dSTing-Kang Chang tinkpb "github.com/google/tink/go/proto/tink_go_proto" 28*e7b1675dSTing-Kang Chang) 29*e7b1675dSTing-Kang Chang 30*e7b1675dSTing-Kang Changconst ( 31*e7b1675dSTing-Kang Chang // HPKE public key length from 32*e7b1675dSTing-Kang Chang // https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1. 33*e7b1675dSTing-Kang Chang hpkeX25519HKDFSHA256PubKeyLen = 32 34*e7b1675dSTing-Kang Chang 35*e7b1675dSTing-Kang Chang hpkePublicKeyTypeURL = "type.googleapis.com/google.crypto.tink.HpkePublicKey" 36*e7b1675dSTing-Kang Chang hpkePrivateKeyTypeURL = "type.googleapis.com/google.crypto.tink.HpkePrivateKey" 37*e7b1675dSTing-Kang Chang) 38*e7b1675dSTing-Kang Chang 39*e7b1675dSTing-Kang Chang// SerializePrimaryPublicKey serializes a public keyset handle's primary key if 40*e7b1675dSTing-Kang Chang// the primary key is a public key and matches both the template argument and a 41*e7b1675dSTing-Kang Chang// supported template. 42*e7b1675dSTing-Kang Chang// 43*e7b1675dSTing-Kang Chang// Supported templates are the same as KeysetHandleFromSerializedPublicKey's: 44*e7b1675dSTing-Kang Chang// - DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305_Raw_Key_Template, 45*e7b1675dSTing-Kang Chang// which returns the KEM-encoding of the public key, i.e. SerializePublicKey 46*e7b1675dSTing-Kang Chang// in https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.1. 47*e7b1675dSTing-Kang Changfunc SerializePrimaryPublicKey(handle *keyset.Handle, template *tinkpb.KeyTemplate) ([]byte, error) { 48*e7b1675dSTing-Kang Chang templateParams, err := hpkeParamsFromTemplate(template) 49*e7b1675dSTing-Kang Chang if err != nil { 50*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("failed to verify key template: %v", err) 51*e7b1675dSTing-Kang Chang } 52*e7b1675dSTing-Kang Chang 53*e7b1675dSTing-Kang Chang // Create keyset from handle. 54*e7b1675dSTing-Kang Chang w := new(bytes.Buffer) 55*e7b1675dSTing-Kang Chang if err := handle.WriteWithNoSecrets(keyset.NewBinaryWriter(w)); err != nil { 56*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("failed to write key: %v", err) 57*e7b1675dSTing-Kang Chang } 58*e7b1675dSTing-Kang Chang ks := &tinkpb.Keyset{} 59*e7b1675dSTing-Kang Chang if err := proto.Unmarshal(w.Bytes(), ks); err != nil { 60*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("failed to unmarshal Keyset %v: %v", ks, err) 61*e7b1675dSTing-Kang Chang } 62*e7b1675dSTing-Kang Chang if len(ks.GetKey()) < 1 { 63*e7b1675dSTing-Kang Chang return nil, errors.New("empty keyset") 64*e7b1675dSTing-Kang Chang } 65*e7b1675dSTing-Kang Chang 66*e7b1675dSTing-Kang Chang // Verify and return handle's primary key. 67*e7b1675dSTing-Kang Chang for _, key := range ks.GetKey() { 68*e7b1675dSTing-Kang Chang if key.GetStatus() != tinkpb.KeyStatusType_ENABLED || 69*e7b1675dSTing-Kang Chang key.GetOutputPrefixType() != tinkpb.OutputPrefixType_RAW || 70*e7b1675dSTing-Kang Chang key.GetKeyId() != ks.GetPrimaryKeyId() { 71*e7b1675dSTing-Kang Chang continue 72*e7b1675dSTing-Kang Chang } 73*e7b1675dSTing-Kang Chang 74*e7b1675dSTing-Kang Chang keyData := key.GetKeyData() 75*e7b1675dSTing-Kang Chang if keyData.GetKeyMaterialType() != tinkpb.KeyData_ASYMMETRIC_PUBLIC { 76*e7b1675dSTing-Kang Chang return nil, errors.New("primary key is not asymmetric public") 77*e7b1675dSTing-Kang Chang } 78*e7b1675dSTing-Kang Chang if keyData.GetTypeUrl() != hpkePublicKeyTypeURL { 79*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("primary key does not have key type URL %s", hpkePublicKeyTypeURL) 80*e7b1675dSTing-Kang Chang } 81*e7b1675dSTing-Kang Chang 82*e7b1675dSTing-Kang Chang hpkeKey := &hpkepb.HpkePublicKey{} 83*e7b1675dSTing-Kang Chang if err := proto.Unmarshal(keyData.GetValue(), hpkeKey); err != nil { 84*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("failed to unmarshal HpkePublicKey %v: %v", hpkeKey, err) 85*e7b1675dSTing-Kang Chang } 86*e7b1675dSTing-Kang Chang // Check equality between HPKE params in handle's primary key and in 87*e7b1675dSTing-Kang Chang // template, as template's params have already been verified. 88*e7b1675dSTing-Kang Chang if !proto.Equal(templateParams, hpkeKey.GetParams()) { 89*e7b1675dSTing-Kang Chang return nil, errors.New("HPKE params in handle and template are not equal") 90*e7b1675dSTing-Kang Chang } 91*e7b1675dSTing-Kang Chang 92*e7b1675dSTing-Kang Chang return hpkeKey.GetPublicKey(), nil 93*e7b1675dSTing-Kang Chang } 94*e7b1675dSTing-Kang Chang 95*e7b1675dSTing-Kang Chang return nil, errors.New("no valid primary HPKE public key in keyset") 96*e7b1675dSTing-Kang Chang} 97*e7b1675dSTing-Kang Chang 98*e7b1675dSTing-Kang Chang// KeysetHandleFromSerializedPublicKey returns a keyset handle containing a 99*e7b1675dSTing-Kang Chang// primary key that has the specified pubKeyBytes and matches template. 100*e7b1675dSTing-Kang Chang// 101*e7b1675dSTing-Kang Chang// Supported templates are the same as PublicKeyFromPrimaryKey's: 102*e7b1675dSTing-Kang Chang// - DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305_Raw_Key_Template, 103*e7b1675dSTing-Kang Chang// which requires pubKeyBytes to be the KEM-encoding of the public key, i.e. 104*e7b1675dSTing-Kang Chang// SerializePublicKey in 105*e7b1675dSTing-Kang Chang// https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.1. 106*e7b1675dSTing-Kang Changfunc KeysetHandleFromSerializedPublicKey(pubKeyBytes []byte, template *tinkpb.KeyTemplate) (*keyset.Handle, error) { 107*e7b1675dSTing-Kang Chang params, err := hpkeParamsFromTemplate(template) 108*e7b1675dSTing-Kang Chang if err != nil { 109*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("failed to verify key template: %v", err) 110*e7b1675dSTing-Kang Chang } 111*e7b1675dSTing-Kang Chang if len(pubKeyBytes) != hpkeX25519HKDFSHA256PubKeyLen { 112*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("pubKeyBytes length is %d but should be %d", len(pubKeyBytes), hpkeX25519HKDFSHA256PubKeyLen) 113*e7b1675dSTing-Kang Chang } 114*e7b1675dSTing-Kang Chang 115*e7b1675dSTing-Kang Chang pubKey := &hpkepb.HpkePublicKey{ 116*e7b1675dSTing-Kang Chang Version: 0, 117*e7b1675dSTing-Kang Chang Params: params, 118*e7b1675dSTing-Kang Chang PublicKey: pubKeyBytes, 119*e7b1675dSTing-Kang Chang } 120*e7b1675dSTing-Kang Chang serializedPubKey, err := proto.Marshal(pubKey) 121*e7b1675dSTing-Kang Chang if err != nil { 122*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("failed to marshal HpkePublicKey %v: %v", pubKey, err) 123*e7b1675dSTing-Kang Chang } 124*e7b1675dSTing-Kang Chang ks := &tinkpb.Keyset{ 125*e7b1675dSTing-Kang Chang PrimaryKeyId: 1, 126*e7b1675dSTing-Kang Chang Key: []*tinkpb.Keyset_Key{ 127*e7b1675dSTing-Kang Chang { 128*e7b1675dSTing-Kang Chang KeyData: &tinkpb.KeyData{ 129*e7b1675dSTing-Kang Chang TypeUrl: hpkePublicKeyTypeURL, 130*e7b1675dSTing-Kang Chang Value: serializedPubKey, 131*e7b1675dSTing-Kang Chang KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC, 132*e7b1675dSTing-Kang Chang }, 133*e7b1675dSTing-Kang Chang Status: tinkpb.KeyStatusType_ENABLED, 134*e7b1675dSTing-Kang Chang KeyId: 1, 135*e7b1675dSTing-Kang Chang OutputPrefixType: tinkpb.OutputPrefixType_RAW, 136*e7b1675dSTing-Kang Chang }, 137*e7b1675dSTing-Kang Chang }, 138*e7b1675dSTing-Kang Chang } 139*e7b1675dSTing-Kang Chang 140*e7b1675dSTing-Kang Chang return keyset.NewHandleWithNoSecrets(ks) 141*e7b1675dSTing-Kang Chang} 142*e7b1675dSTing-Kang Chang 143*e7b1675dSTing-Kang Chang// hpkeParamsFromTemplate returns HPKE params after verifying that template is 144*e7b1675dSTing-Kang Chang// supported. 145*e7b1675dSTing-Kang Chang// 146*e7b1675dSTing-Kang Chang// Supported templates include: 147*e7b1675dSTing-Kang Chang// - DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305_Raw_Key_Template. 148*e7b1675dSTing-Kang Changfunc hpkeParamsFromTemplate(template *tinkpb.KeyTemplate) (*hpkepb.HpkeParams, error) { 149*e7b1675dSTing-Kang Chang if template.GetTypeUrl() != hpkePrivateKeyTypeURL { 150*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("not key type URL %s", hpkePrivateKeyTypeURL) 151*e7b1675dSTing-Kang Chang } 152*e7b1675dSTing-Kang Chang if template.GetOutputPrefixType() != tinkpb.OutputPrefixType_RAW { 153*e7b1675dSTing-Kang Chang return nil, errors.New("not raw output prefix type") 154*e7b1675dSTing-Kang Chang } 155*e7b1675dSTing-Kang Chang keyFormat := &hpkepb.HpkeKeyFormat{} 156*e7b1675dSTing-Kang Chang if err := proto.Unmarshal(template.GetValue(), keyFormat); err != nil { 157*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("failed to unmarshal HpkeKeyFormat(%v): %v", template.GetValue(), err) 158*e7b1675dSTing-Kang Chang } 159*e7b1675dSTing-Kang Chang 160*e7b1675dSTing-Kang Chang params := keyFormat.GetParams() 161*e7b1675dSTing-Kang Chang if kem := params.GetKem(); kem != hpkepb.HpkeKem_DHKEM_X25519_HKDF_SHA256 { 162*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("HPKE KEM %s not supported", kem) 163*e7b1675dSTing-Kang Chang } 164*e7b1675dSTing-Kang Chang if kdf := params.GetKdf(); kdf != hpkepb.HpkeKdf_HKDF_SHA256 { 165*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("HPKE KDF %s not supported", kdf) 166*e7b1675dSTing-Kang Chang } 167*e7b1675dSTing-Kang Chang if aead := params.GetAead(); aead != hpkepb.HpkeAead_CHACHA20_POLY1305 { 168*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("HPKE AEAD %s not supported", aead) 169*e7b1675dSTing-Kang Chang } 170*e7b1675dSTing-Kang Chang 171*e7b1675dSTing-Kang Chang return params, nil 172*e7b1675dSTing-Kang Chang} 173