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