xref: /aosp_15_r20/external/tink/go/hybrid/internal/hpke/x25519_kem.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1// Copyright 2021 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 hpke
18
19import (
20	"fmt"
21
22	"github.com/google/tink/go/subtle"
23)
24
25var (
26	x25519KEMGeneratePrivateKey = subtle.GeneratePrivateKeyX25519
27	x25519KEMPublicFromPrivate  = subtle.PublicFromPrivateX25519
28)
29
30// x25519KEM is a Diffie-Hellman-based X25519 HPKE KEM variant that implements
31// interface kem.
32type x25519KEM struct {
33	// HPKE KEM algorithm identifier.
34	kemID  uint16
35	macAlg string
36}
37
38var _ kem = (*x25519KEM)(nil)
39
40// newX25519KEM constructs a X25519 HPKE KEM using macAlg.
41func newX25519KEM(macAlg string) (*x25519KEM, error) {
42	if macAlg == sha256 {
43		return &x25519KEM{kemID: x25519HKDFSHA256, macAlg: sha256}, nil
44	}
45	return nil, fmt.Errorf("MAC algorithm %s is not supported", macAlg)
46}
47
48func (x *x25519KEM) encapsulate(recipientPubKey []byte) (sharedSecret, senderPubKey []byte, err error) {
49	senderPrivKey, err := x25519KEMGeneratePrivateKey()
50	if err != nil {
51		return nil, nil, err
52	}
53	dh, err := subtle.ComputeSharedSecretX25519(senderPrivKey, recipientPubKey)
54	if err != nil {
55		return nil, nil, err
56	}
57	senderPubKey, err = x25519KEMPublicFromPrivate(senderPrivKey)
58	if err != nil {
59		return nil, nil, err
60	}
61	sharedSecret, err = x.deriveKEMSharedSecret(dh, senderPubKey, recipientPubKey)
62	if err != nil {
63		return nil, nil, err
64	}
65	return sharedSecret, senderPubKey, nil
66}
67
68func (x *x25519KEM) decapsulate(encapsulatedKey, recipientPrivKey []byte) ([]byte, error) {
69	dh, err := subtle.ComputeSharedSecretX25519(recipientPrivKey, encapsulatedKey)
70	if err != nil {
71		return nil, err
72	}
73	recipientPubKey, err := x25519KEMPublicFromPrivate(recipientPrivKey)
74	if err != nil {
75		return nil, err
76	}
77	return x.deriveKEMSharedSecret(dh, encapsulatedKey, recipientPubKey)
78}
79
80func (x *x25519KEM) id() uint16 {
81	return x.kemID
82}
83
84func (x *x25519KEM) encapsulatedKeyLength() int {
85	return 32
86}
87
88// deriveKEMSharedSecret returns a pseudorandom key obtained via HKDF SHA256.
89func (x *x25519KEM) deriveKEMSharedSecret(dh, senderPubKey, recipientPubKey []byte) ([]byte, error) {
90	ctx := make([]byte, 0, len(senderPubKey)+len(recipientPubKey))
91	ctx = append(ctx, senderPubKey...)
92	ctx = append(ctx, recipientPubKey...)
93
94	suiteID := kemSuiteID(x25519HKDFSHA256)
95	macLength, err := subtle.GetHashDigestSize(x.macAlg)
96	if err != nil {
97		return nil, err
98	}
99	hkdfKDF, err := newHKDFKDF(x.macAlg)
100	if err != nil {
101		return nil, err
102	}
103	return hkdfKDF.extractAndExpand(
104		nil, /*=salt*/
105		dh,
106		"eae_prk",
107		ctx,
108		"shared_secret",
109		suiteID,
110		int(macLength))
111}
112