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