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