blob: 304d8591ac219324a028682caef9dafc8aef0cf8 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package hpke
import (
"encoding/hex"
"encoding/json"
"math/big"
"os"
"path/filepath"
"testing"
"github.com/google/tink/go/testutil"
)
// TODO(b/201070904): Separate tests into internal_test package.
// aeadIDs are specified at
// https://www.rfc-editor.org/rfc/rfc9180.html#section-7.3.
var aeadIDs = []struct {
name string
aeadID uint16
keyLength int
}{
{"AES128GCM", aes128GCM, 16},
{"AES256GCM", aes256GCM, 32},
{"ChaCha20Poly1305", chaCha20Poly1305, 32},
}
type hpkeID struct {
id int
mode uint8
kemID uint16
kdfID uint16
aeadID uint16
}
type vector struct {
info []byte
senderPubKey []byte
senderPrivKey []byte
recipientPubKey []byte
recipientPrivKey []byte
encapsulatedKey []byte
sharedSecret []byte
keyScheduleCtx []byte
secret []byte
key []byte
baseNonce []byte
consecutiveEncryptions []encryptionVector
otherEncryptions []encryptionVector
}
type encryptionVector struct {
key []byte
plaintext []byte
associatedData []byte
nonce []byte
ciphertext []byte
sequenceNumber *big.Int
}
type encryptionString struct {
sequenceNumber uint64
plaintext string
associatedData string
nonce string
ciphertext string
}
// TODO(b/201070904): Include all Tink-supported RFC vectors.
func internetDraftVector(t *testing.T) (hpkeID, vector) {
t.Helper()
// Test vector from HPKE RFC
// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1.
v := struct {
mode uint8
kemID, kdfID, aeadID uint16
info, pkEm, skEm, pkRm, skRm, enc, sharedSecret, keyScheduleCtx, secret, key, baseNonce string
consecutiveEncryptions, otherEncryptions []encryptionString
}{
mode: 0,
kemID: 32,
kdfID: 1,
aeadID: 1,
info: "4f6465206f6e2061204772656369616e2055726e",
pkEm: "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431",
skEm: "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736",
pkRm: "3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d",
skRm: "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8",
enc: "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431",
sharedSecret: "fe0e18c9f024ce43799ae393c7e8fe8fce9d218875e8227b0187c04e7d2ea1fc",
keyScheduleCtx: "00725611c9d98c07c03f60095cd32d400d8347d45ed67097bbad50fc56da742d07cb6cffde367bb0565ba28bb02c90744a20f5ef37f30523526106f637abb05449",
secret: "12fff91991e93b48de37e7daddb52981084bd8aa64289c3788471d9a9712f397",
key: "4531685d41d65f03dc48f6b8302c05b0",
baseNonce: "56d890e5accaaf011cff4b7d",
consecutiveEncryptions: []encryptionString{
{
sequenceNumber: 0,
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d30",
nonce: "56d890e5accaaf011cff4b7d",
ciphertext: "f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a",
},
{
sequenceNumber: 1,
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d31",
nonce: "56d890e5accaaf011cff4b7c",
ciphertext: "af2d7e9ac9ae7e270f46ba1f975be53c09f8d875bdc8535458c2494e8a6eab251c03d0c22a56b8ca42c2063b84",
},
{
sequenceNumber: 2,
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d32",
nonce: "56d890e5accaaf011cff4b7f",
ciphertext: "498dfcabd92e8acedc281e85af1cb4e3e31c7dc394a1ca20e173cb72516491588d96a19ad4a683518973dcc180",
},
},
otherEncryptions: []encryptionString{
{
sequenceNumber: 4,
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d34",
nonce: "56d890e5accaaf011cff4b79",
ciphertext: "583bd32bc67a5994bb8ceaca813d369bca7b2a42408cddef5e22f880b631215a09fc0012bc69fccaa251c0246d",
},
{
sequenceNumber: 255,
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d323535",
nonce: "56d890e5accaaf011cff4b82",
ciphertext: "7175db9717964058640a3a11fb9007941a5d1757fda1a6935c805c21af32505bf106deefec4a49ac38d71c9e0a",
},
{
sequenceNumber: 256,
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d323536",
nonce: "56d890e5accaaf011cff4a7d",
ciphertext: "957f9800542b0b8891badb026d79cc54597cb2d225b54c00c5238c25d05c30e3fbeda97d2e0e1aba483a2df9f2",
},
},
}
var info, senderPubKey, senderPrivKey, recipientPubKey, recipientPrivKey, encapsulatedKey, sharedSecret, keyScheduleCtx, secret, key, baseNonce []byte
var err error
if info, err = hex.DecodeString(v.info); err != nil {
t.Fatalf("hex.DecodeString(info): err %q", err)
}
if senderPubKey, err = hex.DecodeString(v.pkEm); err != nil {
t.Fatalf("hex.DecodeString(pkEm): err %q", err)
}
if senderPrivKey, err = hex.DecodeString(v.skEm); err != nil {
t.Fatalf("hex.DecodeString(skEm): err %q", err)
}
if recipientPubKey, err = hex.DecodeString(v.pkRm); err != nil {
t.Fatalf("hex.DecodeString(pkRm): err %q", err)
}
if recipientPrivKey, err = hex.DecodeString(v.skRm); err != nil {
t.Fatalf("hex.DecodeString(skRm): err %q", err)
}
if encapsulatedKey, err = hex.DecodeString(v.enc); err != nil {
t.Fatalf("hex.DecodeString(enc): err %q", err)
}
if sharedSecret, err = hex.DecodeString(v.sharedSecret); err != nil {
t.Fatalf("hex.DecodeString(sharedSecret): err %q", err)
}
if keyScheduleCtx, err = hex.DecodeString(v.keyScheduleCtx); err != nil {
t.Fatalf("hex.DecodeString(keyScheduleCtx): err %q", err)
}
if secret, err = hex.DecodeString(v.secret); err != nil {
t.Fatalf("hex.DecodeString(secret): err %q", err)
}
if key, err = hex.DecodeString(v.key); err != nil {
t.Fatalf("hex.DecodeString(key): err %q", err)
}
if baseNonce, err = hex.DecodeString(v.baseNonce); err != nil {
t.Fatalf("hex.DecodeString(baseNonce): err %q", err)
}
return hpkeID{0 /*=id */, v.mode, v.kemID, v.kdfID, v.aeadID},
vector{
info: info,
senderPubKey: senderPubKey,
senderPrivKey: senderPrivKey,
recipientPubKey: recipientPubKey,
recipientPrivKey: recipientPrivKey,
encapsulatedKey: encapsulatedKey,
sharedSecret: sharedSecret,
keyScheduleCtx: keyScheduleCtx,
secret: secret,
key: key,
baseNonce: baseNonce,
consecutiveEncryptions: parseEncryptions(t, v.consecutiveEncryptions),
otherEncryptions: parseEncryptions(t, v.otherEncryptions),
}
}
func parseEncryptions(t *testing.T, encs []encryptionString) []encryptionVector {
t.Helper()
var res []encryptionVector
for _, e := range encs {
var plaintext, associatedData, nonce, ciphertext []byte
var err error
if plaintext, err = hex.DecodeString(e.plaintext); err != nil {
t.Fatalf("hex.DecodeString(plaintext): err %q", err)
}
if associatedData, err = hex.DecodeString(e.associatedData); err != nil {
t.Fatalf("hex.DecodeString(associatedData): err %q", err)
}
if nonce, err = hex.DecodeString(e.nonce); err != nil {
t.Fatalf("hex.DecodeString(nonce): err %q", err)
}
if ciphertext, err = hex.DecodeString(e.ciphertext); err != nil {
t.Fatalf("hex.DecodeString(ciphertext): err %q", err)
}
res = append(res, encryptionVector{
plaintext: plaintext,
associatedData: associatedData,
nonce: nonce,
ciphertext: ciphertext,
sequenceNumber: big.NewInt(int64(e.sequenceNumber)),
})
}
return res
}
// aeadRFCVectors returns RFC test vectors for AEAD IDs aes128GCM, aes256GCM,
// and chaCha20Poly1305.
func aeadRFCVectors(t *testing.T) map[hpkeID]encryptionVector {
t.Helper()
vecs := []struct {
mode uint8
kemID, kdfID, aeadID uint16
key, plaintext, associatedData, nonce, ciphertext string
}{
// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1.1
{
mode: 0,
kemID: 32,
kdfID: 1,
aeadID: 1,
key: "4531685d41d65f03dc48f6b8302c05b0",
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d30",
nonce: "56d890e5accaaf011cff4b7d",
ciphertext: "f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a",
},
{
mode: 0,
kemID: 32,
kdfID: 1,
aeadID: 1,
key: "4531685d41d65f03dc48f6b8302c05b0",
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d31",
nonce: "56d890e5accaaf011cff4b7c",
ciphertext: "af2d7e9ac9ae7e270f46ba1f975be53c09f8d875bdc8535458c2494e8a6eab251c03d0c22a56b8ca42c2063b84",
},
// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.6.1.1
{
mode: 0,
kemID: 18,
kdfID: 3,
aeadID: 2,
key: "751e346ce8f0ddb2305c8a2a85c70d5cf559c53093656be636b9406d4d7d1b70",
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d30",
nonce: "55ff7a7d739c69f44b25447b",
ciphertext: "170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a",
},
{
mode: 0,
kemID: 18,
kdfID: 3,
aeadID: 2,
key: "751e346ce8f0ddb2305c8a2a85c70d5cf559c53093656be636b9406d4d7d1b70",
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d31",
nonce: "55ff7a7d739c69f44b25447a",
ciphertext: "d9ee248e220ca24ac00bbbe7e221a832e4f7fa64c4fbab3945b6f3af0c5ecd5e16815b328be4954a05fd352256",
},
// https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.2.1.1
{
mode: 0,
kemID: 32,
kdfID: 1,
aeadID: 3,
key: "ad2744de8e17f4ebba575b3f5f5a8fa1f69c2a07f6e7500bc60ca6e3e3ec1c91",
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d30",
nonce: "5c4d98150661b848853b547f",
ciphertext: "1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28",
},
{
mode: 0,
kemID: 32,
kdfID: 1,
aeadID: 3,
key: "ad2744de8e17f4ebba575b3f5f5a8fa1f69c2a07f6e7500bc60ca6e3e3ec1c91",
plaintext: "4265617574792069732074727574682c20747275746820626561757479",
associatedData: "436f756e742d31",
nonce: "5c4d98150661b848853b547e",
ciphertext: "6b53c051e4199c518de79594e1c4ab18b96f081549d45ce015be002090bb119e85285337cc95ba5f59992dc98c",
},
}
m := make(map[hpkeID]encryptionVector)
for i, v := range vecs {
var key, plaintext, associatedData, nonce, ciphertext []byte
var err error
if key, err = hex.DecodeString(v.key); err != nil {
t.Fatalf("hex.DecodeString(key): err %q", err)
}
if plaintext, err = hex.DecodeString(v.plaintext); err != nil {
t.Fatalf("hex.DecodeString(plaintext): err %q", err)
}
if associatedData, err = hex.DecodeString(v.associatedData); err != nil {
t.Fatalf("hex.DecodeString(associatedData): err %q", err)
}
if nonce, err = hex.DecodeString(v.nonce); err != nil {
t.Fatalf("hex.DecodeString(nonce): err %q", err)
}
if ciphertext, err = hex.DecodeString(v.ciphertext); err != nil {
t.Fatalf("hex.DecodeString(ciphertext): err %q", err)
}
id := hpkeID{i, v.mode, v.kemID, v.kdfID, v.aeadID}
m[id] = encryptionVector{
key: key,
plaintext: plaintext,
associatedData: associatedData,
nonce: nonce,
ciphertext: ciphertext,
}
}
return m
}
// baseModeX25519HKDFSHA256Vectors returns BoringSSL test vectors for HPKE base
// mode with Diffie-Hellman-based X25519, HKDF-SHA256 KEM as per
// https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.
func baseModeX25519HKDFSHA256Vectors(t *testing.T) map[hpkeID]vector {
testutil.SkipTestIfTestSrcDirIsNotSet(t)
t.Helper()
srcDir, _ := os.LookupEnv("TEST_SRCDIR")
path := filepath.Join(srcDir, os.Getenv("TEST_WORKSPACE"), "/testdata/testvectors/hpke_boringssl.json")
f, err := os.Open(path)
if err != nil {
t.Fatal(err)
}
var vecs []struct {
Mode uint8 `json:"mode"`
KEMID uint16 `json:"kem_id"`
KDFID uint16 `json:"kdf_id"`
AEADID uint16 `json:"aead_id"`
Info testutil.HexBytes `json:"info"`
SenderPubKey testutil.HexBytes `json:"pkEm"`
SenderPrivKey testutil.HexBytes `json:"skEm"`
RecipientPubKey testutil.HexBytes `json:"pkRm"`
RecipientPrivKey testutil.HexBytes `json:"skRm"`
EncapsulatedKey testutil.HexBytes `json:"enc"`
SharedSecret testutil.HexBytes `json:"shared_secret"`
KeyScheduleCtx testutil.HexBytes `json:"key_schedule_context"`
Secret testutil.HexBytes `json:"secret"`
Key testutil.HexBytes `json:"key"`
BaseNonce testutil.HexBytes `json:"base_nonce"`
}
parser := json.NewDecoder(f)
if err := parser.Decode(&vecs); err != nil {
t.Fatal(err)
}
m := make(map[hpkeID]vector)
for i, v := range vecs {
if v.Mode != baseMode || v.KEMID != x25519HKDFSHA256 {
continue
}
id := hpkeID{i, v.Mode, v.KEMID, v.KDFID, v.AEADID}
m[id] = vector{
info: v.Info,
senderPubKey: v.SenderPubKey,
senderPrivKey: v.SenderPrivKey,
recipientPubKey: v.RecipientPubKey,
recipientPrivKey: v.RecipientPrivKey,
encapsulatedKey: v.EncapsulatedKey,
sharedSecret: v.SharedSecret,
keyScheduleCtx: v.KeyScheduleCtx,
secret: v.Secret,
key: v.Key,
baseNonce: v.BaseNonce,
}
}
return m
}