xref: /aosp_15_r20/external/tink/go/hybrid/subtle/public_key.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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