xref: /aosp_15_r20/external/tink/go/hybrid/internal/hpke/context.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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 hpke
18*e7b1675dSTing-Kang Chang
19*e7b1675dSTing-Kang Changimport (
20*e7b1675dSTing-Kang Chang	"errors"
21*e7b1675dSTing-Kang Chang	"fmt"
22*e7b1675dSTing-Kang Chang	"math/big"
23*e7b1675dSTing-Kang Chang
24*e7b1675dSTing-Kang Chang	pb "github.com/google/tink/go/proto/hpke_go_proto"
25*e7b1675dSTing-Kang Chang)
26*e7b1675dSTing-Kang Chang
27*e7b1675dSTing-Kang Changtype context struct {
28*e7b1675dSTing-Kang Chang	aead              aead
29*e7b1675dSTing-Kang Chang	maxSequenceNumber *big.Int
30*e7b1675dSTing-Kang Chang	sequenceNumber    *big.Int
31*e7b1675dSTing-Kang Chang	key               []byte
32*e7b1675dSTing-Kang Chang	baseNonce         []byte
33*e7b1675dSTing-Kang Chang	encapsulatedKey   []byte
34*e7b1675dSTing-Kang Chang}
35*e7b1675dSTing-Kang Chang
36*e7b1675dSTing-Kang Chang// newSenderContext creates the HPKE sender context as per KeySchedule()
37*e7b1675dSTing-Kang Chang// https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1-10.
38*e7b1675dSTing-Kang Changfunc newSenderContext(recipientPubKey *pb.HpkePublicKey, kem kem, kdf kdf, aead aead, info []byte) (*context, error) {
39*e7b1675dSTing-Kang Chang	if recipientPubKey.GetPublicKey() == nil {
40*e7b1675dSTing-Kang Chang		return nil, errors.New("HpkePublicKey has an empty PublicKey")
41*e7b1675dSTing-Kang Chang	}
42*e7b1675dSTing-Kang Chang	sharedSecret, encapsulatedKey, err := kem.encapsulate(recipientPubKey.GetPublicKey())
43*e7b1675dSTing-Kang Chang	if err != nil {
44*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("encapsulate: %v", err)
45*e7b1675dSTing-Kang Chang	}
46*e7b1675dSTing-Kang Chang	return createContext(encapsulatedKey, sharedSecret, kem, kdf, aead, info)
47*e7b1675dSTing-Kang Chang}
48*e7b1675dSTing-Kang Chang
49*e7b1675dSTing-Kang Chang// newRecipientContext creates the HPKE recipient context as per KeySchedule()
50*e7b1675dSTing-Kang Chang// https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1-10.
51*e7b1675dSTing-Kang Changfunc newRecipientContext(encapsulatedKey []byte, recipientPrivKey *pb.HpkePrivateKey, kem kem, kdf kdf, aead aead, info []byte) (*context, error) {
52*e7b1675dSTing-Kang Chang	if recipientPrivKey.GetPrivateKey() == nil {
53*e7b1675dSTing-Kang Chang		return nil, errors.New("HpkePrivateKey has an empty PrivateKey")
54*e7b1675dSTing-Kang Chang	}
55*e7b1675dSTing-Kang Chang	sharedSecret, err := kem.decapsulate(encapsulatedKey, recipientPrivKey.GetPrivateKey())
56*e7b1675dSTing-Kang Chang	if err != nil {
57*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("decapsulate: %v", err)
58*e7b1675dSTing-Kang Chang	}
59*e7b1675dSTing-Kang Chang	return createContext(encapsulatedKey, sharedSecret, kem, kdf, aead, info)
60*e7b1675dSTing-Kang Chang}
61*e7b1675dSTing-Kang Chang
62*e7b1675dSTing-Kang Changfunc createContext(encapsulatedKey []byte, sharedSecret []byte, kem kem, kdf kdf, aead aead, info []byte) (*context, error) {
63*e7b1675dSTing-Kang Chang	suiteID := hpkeSuiteID(kem.id(), kdf.id(), aead.id())
64*e7b1675dSTing-Kang Chang	// In base mode, both the pre-shared key (default_psk) and pre-shared key ID
65*e7b1675dSTing-Kang Chang	// (default_psk_id) are empty strings, see
66*e7b1675dSTing-Kang Chang	// https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.1-4.
67*e7b1675dSTing-Kang Chang	pskIDHash := kdf.labeledExtract(emptySalt, emptyIKM /*= default PSK ID*/, "psk_id_hash", suiteID)
68*e7b1675dSTing-Kang Chang	infoHash := kdf.labeledExtract(emptySalt, info, "info_hash", suiteID)
69*e7b1675dSTing-Kang Chang	keyScheduleCtx := keyScheduleContext(baseMode, pskIDHash, infoHash)
70*e7b1675dSTing-Kang Chang	secret := kdf.labeledExtract(sharedSecret, emptyIKM /*= default PSK*/, "secret", suiteID)
71*e7b1675dSTing-Kang Chang
72*e7b1675dSTing-Kang Chang	key, err := kdf.labeledExpand(secret, keyScheduleCtx, "key", suiteID, aead.keyLength())
73*e7b1675dSTing-Kang Chang	if err != nil {
74*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("labeledExpand of key: %v", err)
75*e7b1675dSTing-Kang Chang	}
76*e7b1675dSTing-Kang Chang	baseNonce, err := kdf.labeledExpand(secret, keyScheduleCtx, "base_nonce", suiteID, aead.nonceLength())
77*e7b1675dSTing-Kang Chang	if err != nil {
78*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("labeledExpand of base nonce: %v", err)
79*e7b1675dSTing-Kang Chang	}
80*e7b1675dSTing-Kang Chang
81*e7b1675dSTing-Kang Chang	return &context{
82*e7b1675dSTing-Kang Chang		aead:              aead,
83*e7b1675dSTing-Kang Chang		maxSequenceNumber: maxSequenceNumber(aead.nonceLength()),
84*e7b1675dSTing-Kang Chang		sequenceNumber:    big.NewInt(0),
85*e7b1675dSTing-Kang Chang		key:               key,
86*e7b1675dSTing-Kang Chang		baseNonce:         baseNonce,
87*e7b1675dSTing-Kang Chang		encapsulatedKey:   encapsulatedKey,
88*e7b1675dSTing-Kang Chang	}, nil
89*e7b1675dSTing-Kang Chang}
90*e7b1675dSTing-Kang Chang
91*e7b1675dSTing-Kang Chang// maxSequenceNumber returns the maximum sequence number indicating that the
92*e7b1675dSTing-Kang Chang// message limit is reached, calculated as per
93*e7b1675dSTing-Kang Chang// https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2-11.
94*e7b1675dSTing-Kang Changfunc maxSequenceNumber(nonceLength int) *big.Int {
95*e7b1675dSTing-Kang Chang	res := new(big.Int)
96*e7b1675dSTing-Kang Chang	one := big.NewInt(1)
97*e7b1675dSTing-Kang Chang	res.Lsh(one, uint(8*nonceLength)).Sub(res, one)
98*e7b1675dSTing-Kang Chang	return res
99*e7b1675dSTing-Kang Chang}
100*e7b1675dSTing-Kang Chang
101*e7b1675dSTing-Kang Changfunc (c *context) incrementSequenceNumber() error {
102*e7b1675dSTing-Kang Chang	c.sequenceNumber.Add(c.sequenceNumber, big.NewInt(1))
103*e7b1675dSTing-Kang Chang	if c.sequenceNumber.Cmp(c.maxSequenceNumber) > 0 {
104*e7b1675dSTing-Kang Chang		return errors.New("message limit reached")
105*e7b1675dSTing-Kang Chang	}
106*e7b1675dSTing-Kang Chang	return nil
107*e7b1675dSTing-Kang Chang}
108*e7b1675dSTing-Kang Chang
109*e7b1675dSTing-Kang Chang// computeNonce computes the nonce as per
110*e7b1675dSTing-Kang Chang// https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2-12.
111*e7b1675dSTing-Kang Changfunc (c *context) computeNonce() ([]byte, error) {
112*e7b1675dSTing-Kang Chang	nonce := make([]byte, len(c.baseNonce))
113*e7b1675dSTing-Kang Chang
114*e7b1675dSTing-Kang Chang	// Write the big-endian c.sequenceNumber value at the end of nonce.
115*e7b1675dSTing-Kang Chang	sequenceNumber := c.sequenceNumber.Bytes()
116*e7b1675dSTing-Kang Chang	index := len(nonce) - len(sequenceNumber)
117*e7b1675dSTing-Kang Chang	if index < 0 {
118*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("sequence number length (%d) is larger than nonce length (%d)", len(sequenceNumber), len(nonce))
119*e7b1675dSTing-Kang Chang	}
120*e7b1675dSTing-Kang Chang	copy(nonce[index:], sequenceNumber)
121*e7b1675dSTing-Kang Chang
122*e7b1675dSTing-Kang Chang	// nonce XOR c.baseNonce.
123*e7b1675dSTing-Kang Chang	for i, b := range c.baseNonce {
124*e7b1675dSTing-Kang Chang		nonce[i] ^= b
125*e7b1675dSTing-Kang Chang	}
126*e7b1675dSTing-Kang Chang
127*e7b1675dSTing-Kang Chang	return nonce, nil
128*e7b1675dSTing-Kang Chang}
129*e7b1675dSTing-Kang Chang
130*e7b1675dSTing-Kang Chang// seal allows the sender's context to encrypt plaintext with associatedData,
131*e7b1675dSTing-Kang Chang// defined as ContextS.Seal in
132*e7b1675dSTing-Kang Chang// https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2-7.
133*e7b1675dSTing-Kang Changfunc (c *context) seal(plaintext, associatedData []byte) ([]byte, error) {
134*e7b1675dSTing-Kang Chang	nonce, err := c.computeNonce()
135*e7b1675dSTing-Kang Chang	if err != nil {
136*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("computeNonce: %v", err)
137*e7b1675dSTing-Kang Chang	}
138*e7b1675dSTing-Kang Chang	ciphertext, err := c.aead.seal(c.key, nonce, plaintext, associatedData)
139*e7b1675dSTing-Kang Chang	if err != nil {
140*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("seal: %v", err)
141*e7b1675dSTing-Kang Chang	}
142*e7b1675dSTing-Kang Chang	if err := c.incrementSequenceNumber(); err != nil {
143*e7b1675dSTing-Kang Chang		return nil, err
144*e7b1675dSTing-Kang Chang	}
145*e7b1675dSTing-Kang Chang	return ciphertext, nil
146*e7b1675dSTing-Kang Chang}
147*e7b1675dSTing-Kang Chang
148*e7b1675dSTing-Kang Chang// open allows the receiver's context to decrypt ciphertext with
149*e7b1675dSTing-Kang Chang// associatedData, defined as ContextR.Open in
150*e7b1675dSTing-Kang Chang// https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2-9.
151*e7b1675dSTing-Kang Changfunc (c *context) open(ciphertext, associatedData []byte) ([]byte, error) {
152*e7b1675dSTing-Kang Chang	nonce, err := c.computeNonce()
153*e7b1675dSTing-Kang Chang	if err != nil {
154*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("computeNonce: %v", err)
155*e7b1675dSTing-Kang Chang	}
156*e7b1675dSTing-Kang Chang	plaintext, err := c.aead.open(c.key, nonce, ciphertext, associatedData)
157*e7b1675dSTing-Kang Chang	if err != nil {
158*e7b1675dSTing-Kang Chang		return nil, fmt.Errorf("open: %v", err)
159*e7b1675dSTing-Kang Chang	}
160*e7b1675dSTing-Kang Chang	if err := c.incrementSequenceNumber(); err != nil {
161*e7b1675dSTing-Kang Chang		return nil, err
162*e7b1675dSTing-Kang Chang	}
163*e7b1675dSTing-Kang Chang	return plaintext, nil
164*e7b1675dSTing-Kang Chang}
165