| // Copyright 2022 Google LLC |
| // |
| // 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. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package subtle |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| |
| "google.golang.org/protobuf/proto" |
| "github.com/google/tink/go/keyset" |
| hpkepb "github.com/google/tink/go/proto/hpke_go_proto" |
| tinkpb "github.com/google/tink/go/proto/tink_go_proto" |
| ) |
| |
| const ( |
| // HPKE public key length from |
| // https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1. |
| hpkeX25519HKDFSHA256PubKeyLen = 32 |
| |
| hpkePublicKeyTypeURL = "type.googleapis.com/google.crypto.tink.HpkePublicKey" |
| hpkePrivateKeyTypeURL = "type.googleapis.com/google.crypto.tink.HpkePrivateKey" |
| ) |
| |
| // SerializePrimaryPublicKey serializes a public keyset handle's primary key if |
| // the primary key is a public key and matches both the template argument and a |
| // supported template. |
| // |
| // Supported templates are the same as KeysetHandleFromSerializedPublicKey's: |
| // - DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305_Raw_Key_Template, |
| // which returns the KEM-encoding of the public key, i.e. SerializePublicKey |
| // in https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.1. |
| func SerializePrimaryPublicKey(handle *keyset.Handle, template *tinkpb.KeyTemplate) ([]byte, error) { |
| templateParams, err := hpkeParamsFromTemplate(template) |
| if err != nil { |
| return nil, fmt.Errorf("failed to verify key template: %v", err) |
| } |
| |
| // Create keyset from handle. |
| w := new(bytes.Buffer) |
| if err := handle.WriteWithNoSecrets(keyset.NewBinaryWriter(w)); err != nil { |
| return nil, fmt.Errorf("failed to write key: %v", err) |
| } |
| ks := &tinkpb.Keyset{} |
| if err := proto.Unmarshal(w.Bytes(), ks); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal Keyset %v: %v", ks, err) |
| } |
| if len(ks.GetKey()) < 1 { |
| return nil, errors.New("empty keyset") |
| } |
| |
| // Verify and return handle's primary key. |
| for _, key := range ks.GetKey() { |
| if key.GetStatus() != tinkpb.KeyStatusType_ENABLED || |
| key.GetOutputPrefixType() != tinkpb.OutputPrefixType_RAW || |
| key.GetKeyId() != ks.GetPrimaryKeyId() { |
| continue |
| } |
| |
| keyData := key.GetKeyData() |
| if keyData.GetKeyMaterialType() != tinkpb.KeyData_ASYMMETRIC_PUBLIC { |
| return nil, errors.New("primary key is not asymmetric public") |
| } |
| if keyData.GetTypeUrl() != hpkePublicKeyTypeURL { |
| return nil, fmt.Errorf("primary key does not have key type URL %s", hpkePublicKeyTypeURL) |
| } |
| |
| hpkeKey := &hpkepb.HpkePublicKey{} |
| if err := proto.Unmarshal(keyData.GetValue(), hpkeKey); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal HpkePublicKey %v: %v", hpkeKey, err) |
| } |
| // Check equality between HPKE params in handle's primary key and in |
| // template, as template's params have already been verified. |
| if !proto.Equal(templateParams, hpkeKey.GetParams()) { |
| return nil, errors.New("HPKE params in handle and template are not equal") |
| } |
| |
| return hpkeKey.GetPublicKey(), nil |
| } |
| |
| return nil, errors.New("no valid primary HPKE public key in keyset") |
| } |
| |
| // KeysetHandleFromSerializedPublicKey returns a keyset handle containing a |
| // primary key that has the specified pubKeyBytes and matches template. |
| // |
| // Supported templates are the same as PublicKeyFromPrimaryKey's: |
| // - DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305_Raw_Key_Template, |
| // which requires pubKeyBytes to be the KEM-encoding of the public key, i.e. |
| // SerializePublicKey in |
| // https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.1. |
| func KeysetHandleFromSerializedPublicKey(pubKeyBytes []byte, template *tinkpb.KeyTemplate) (*keyset.Handle, error) { |
| params, err := hpkeParamsFromTemplate(template) |
| if err != nil { |
| return nil, fmt.Errorf("failed to verify key template: %v", err) |
| } |
| if len(pubKeyBytes) != hpkeX25519HKDFSHA256PubKeyLen { |
| return nil, fmt.Errorf("pubKeyBytes length is %d but should be %d", len(pubKeyBytes), hpkeX25519HKDFSHA256PubKeyLen) |
| } |
| |
| pubKey := &hpkepb.HpkePublicKey{ |
| Version: 0, |
| Params: params, |
| PublicKey: pubKeyBytes, |
| } |
| serializedPubKey, err := proto.Marshal(pubKey) |
| if err != nil { |
| return nil, fmt.Errorf("failed to marshal HpkePublicKey %v: %v", pubKey, err) |
| } |
| ks := &tinkpb.Keyset{ |
| PrimaryKeyId: 1, |
| Key: []*tinkpb.Keyset_Key{ |
| { |
| KeyData: &tinkpb.KeyData{ |
| TypeUrl: hpkePublicKeyTypeURL, |
| Value: serializedPubKey, |
| KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PUBLIC, |
| }, |
| Status: tinkpb.KeyStatusType_ENABLED, |
| KeyId: 1, |
| OutputPrefixType: tinkpb.OutputPrefixType_RAW, |
| }, |
| }, |
| } |
| |
| return keyset.NewHandleWithNoSecrets(ks) |
| } |
| |
| // hpkeParamsFromTemplate returns HPKE params after verifying that template is |
| // supported. |
| // |
| // Supported templates include: |
| // - DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305_Raw_Key_Template. |
| func hpkeParamsFromTemplate(template *tinkpb.KeyTemplate) (*hpkepb.HpkeParams, error) { |
| if template.GetTypeUrl() != hpkePrivateKeyTypeURL { |
| return nil, fmt.Errorf("not key type URL %s", hpkePrivateKeyTypeURL) |
| } |
| if template.GetOutputPrefixType() != tinkpb.OutputPrefixType_RAW { |
| return nil, errors.New("not raw output prefix type") |
| } |
| keyFormat := &hpkepb.HpkeKeyFormat{} |
| if err := proto.Unmarshal(template.GetValue(), keyFormat); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal HpkeKeyFormat(%v): %v", template.GetValue(), err) |
| } |
| |
| params := keyFormat.GetParams() |
| if kem := params.GetKem(); kem != hpkepb.HpkeKem_DHKEM_X25519_HKDF_SHA256 { |
| return nil, fmt.Errorf("HPKE KEM %s not supported", kem) |
| } |
| if kdf := params.GetKdf(); kdf != hpkepb.HpkeKdf_HKDF_SHA256 { |
| return nil, fmt.Errorf("HPKE KDF %s not supported", kdf) |
| } |
| if aead := params.GetAead(); aead != hpkepb.HpkeAead_CHACHA20_POLY1305 { |
| return nil, fmt.Errorf("HPKE AEAD %s not supported", aead) |
| } |
| |
| return params, nil |
| } |