acvp: add HKDF support.

Change-Id: I26251ce85f2cb1b441ae415b1506161a90bd3efa
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48585
Reviewed-by: David Benjamin <davidben@google.com>
grpc-202302
Adam Langley 4 years ago committed by Adam Langley
parent 7a817f48ba
commit c1571feb5f
  1. 1
      util/fipstools/acvp/ACVP.md
  2. 203
      util/fipstools/acvp/acvptool/subprocess/hkdf.go
  3. 1
      util/fipstools/acvp/acvptool/subprocess/subprocess.go
  4. BIN
      util/fipstools/acvp/acvptool/test/expected/KAS-KDF.bz2
  5. 1
      util/fipstools/acvp/acvptool/test/tests.json
  6. BIN
      util/fipstools/acvp/acvptool/test/vectors/KAS-KDF.bz2
  7. 58
      util/fipstools/acvp/acvptool/testmodulewrapper/testmodulewrapper.go

@ -72,6 +72,7 @@ The other commands are as follows. (Note that you only need to implement the com
| ECDSA/sigGen | Curve name, private key, hash name, message | R, S |
| ECDSA/sigVer | Curve name, hash name, message, X, Y, R, S | Single-byte validity flag |
| FFDH | p, q, g, peer public key, local private key (or empty), local public key (or empty) | Local public key, shared key |
| HKDF/&lt;HASH&gt; | key, salt, info, num output bytes | Key |
| HMAC-SHA-1 | Value to hash, key | Digest |
| HMAC-SHA2-224 | Value to hash, key | Digest |
| HMAC-SHA2-256 | Value to hash, key | Digest |

@ -0,0 +1,203 @@
// Copyright (c) 2021, Google Inc.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package subprocess
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
)
// The following structures reflect the JSON of ACVP KAS KDF tests. See
// https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-kdf-twostep.html
type hkdfTestVectorSet struct {
Groups []hkdfTestGroup `json:"testGroups"`
}
type hkdfTestGroup struct {
ID uint64 `json:"tgId"`
Type string `json:"testType"` // AFT or VAL
Config hkdfConfiguration `json:"kdfConfiguration"`
Tests []hkdfTest `json:"tests"`
}
type hkdfTest struct {
ID uint64 `json:"tcId"`
Params hkdfParameters `json:"kdfParameter"`
PartyU hkdfPartyInfo `json:"fixedInfoPartyU"`
PartyV hkdfPartyInfo `json:"fixedInfoPartyV"`
ExpectedHex string `json:"dkm"`
}
type hkdfConfiguration struct {
Type string `json:"kdfType"`
AdditionalNonce bool `json:"requiresAdditionalNoncePair"`
OutputBits uint32 `json:"l"`
FixedInfoPattern string `json:"fixedInfoPattern"`
FixedInputEncoding string `json:"fixedInfoEncoding"`
KDFMode string `json:"kdfMode"`
MACMode string `json:"macMode"`
CounterLocation string `json:"counterLocation"`
CounterBits uint `json:"counterLen"`
}
func (c *hkdfConfiguration) extract() (outBytes uint32, hashName string, err error) {
if c.Type != "twoStep" ||
c.AdditionalNonce ||
c.FixedInfoPattern != "uPartyInfo||vPartyInfo" ||
c.FixedInputEncoding != "concatenation" ||
c.KDFMode != "feedback" ||
c.CounterLocation != "after fixed data" ||
c.CounterBits != 8 ||
c.OutputBits%8 != 0 {
return 0, "", fmt.Errorf("KAS-KDF not configured for HKDF: %#v", c)
}
if !strings.HasPrefix(c.MACMode, "HMAC-") {
return 0, "", fmt.Errorf("MAC mode %q does't start with 'HMAC-'", c.MACMode)
}
return c.OutputBits / 8, c.MACMode[5:], nil
}
type hkdfParameters struct {
SaltHex string `json:"salt"`
KeyHex string `json:"z"`
}
func (p *hkdfParameters) extract() (key, salt []byte, err error) {
salt, err = hex.DecodeString(p.SaltHex)
if err != nil {
return nil, nil, err
}
key, err = hex.DecodeString(p.KeyHex)
if err != nil {
return nil, nil, err
}
return key, salt, nil
}
type hkdfPartyInfo struct {
IDHex string `json:"partyId"`
ExtraHex string `json:"ephemeralData"`
}
func (p *hkdfPartyInfo) data() ([]byte, error) {
ret, err := hex.DecodeString(p.IDHex)
if err != nil {
return nil, err
}
if len(p.ExtraHex) > 0 {
extra, err := hex.DecodeString(p.ExtraHex)
if err != nil {
return nil, err
}
ret = append(ret, extra...)
}
return ret, nil
}
type hkdfTestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []hkdfTestResponse `json:"tests"`
}
type hkdfTestResponse struct {
ID uint64 `json:"tcId"`
KeyOut string `json:"dkm,omitempty"`
Passed *bool `json:"testPassed,omitempty"`
}
type hkdf struct{}
func (k *hkdf) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed hkdfTestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
}
var respGroups []hkdfTestGroupResponse
for _, group := range parsed.Groups {
groupResp := hkdfTestGroupResponse{ID: group.ID}
var isValidationTest bool
switch group.Type {
case "VAL":
isValidationTest = true
case "AFT":
isValidationTest = false
default:
return nil, fmt.Errorf("unknown test type %q", group.Type)
}
outBytes, hashName, err := group.Config.extract()
if err != nil {
return nil, err
}
for _, test := range group.Tests {
testResp := hkdfTestResponse{ID: test.ID}
key, salt, err := test.Params.extract()
if err != nil {
return nil, err
}
uData, err := test.PartyU.data()
if err != nil {
return nil, err
}
vData, err := test.PartyV.data()
if err != nil {
return nil, err
}
var expected []byte
if isValidationTest {
expected, err = hex.DecodeString(test.ExpectedHex)
if err != nil {
return nil, err
}
}
info := make([]byte, 0, len(uData)+len(vData))
info = append(info, uData...)
info = append(info, vData...)
resp, err := m.Transact("HKDF/"+hashName, 1, key, salt, info, uint32le(outBytes))
if err != nil {
return nil, fmt.Errorf("HKDF operation failed: %s", err)
}
if isValidationTest {
passed := bytes.Equal(expected, resp[0])
testResp.Passed = &passed
} else {
testResp.KeyOut = hex.EncodeToString(resp[0])
}
groupResp.Tests = append(groupResp.Tests, testResp)
}
respGroups = append(respGroups, groupResp)
}
return respGroups, nil
}

@ -96,6 +96,7 @@ func NewWithIO(cmd *exec.Cmd, in io.WriteCloser, out io.ReadCloser) *Subprocess
"ctrDRBG": &drbg{"ctrDRBG", map[string]bool{"AES-128": true, "AES-192": true, "AES-256": true}},
"hmacDRBG": &drbg{"hmacDRBG", map[string]bool{"SHA-1": true, "SHA2-224": true, "SHA2-256": true, "SHA2-384": true, "SHA2-512": true}},
"KDF": &kdfPrimitive{},
"KAS-KDF": &hkdf{},
"CMAC-AES": &keyedMACPrimitive{"CMAC-AES"},
"RSA": &rsa{},
"kdf-components": &tlsKDF{},

@ -18,6 +18,7 @@
{"Wrapper": "modulewrapper", "In": "vectors/HMAC-SHA2-256.bz2", "Out": "expected/HMAC-SHA2-256.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/HMAC-SHA2-384.bz2", "Out": "expected/HMAC-SHA2-384.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/HMAC-SHA2-512.bz2", "Out": "expected/HMAC-SHA2-512.bz2"},
{"Wrapper": "testmodulewrapper", "In": "vectors/KAS-KDF.bz2", "Out": "expected/KAS-KDF.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KAS-ECC-SSC.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KAS-FFC-SSC.bz2"},
{"Wrapper": "testmodulewrapper", "In": "vectors/KDF.bz2"},

@ -16,6 +16,7 @@ import (
"io"
"os"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/xts"
)
@ -24,6 +25,7 @@ var handlers = map[string]func([][]byte) error{
"KDF-counter": kdfCounter,
"AES-XTS/encrypt": xtsEncrypt,
"AES-XTS/decrypt": xtsDecrypt,
"HKDF/SHA2-256": hkdfMAC,
}
func getConfig(args [][]byte) error {
@ -69,6 +71,39 @@ func getConfig(args [][]byte) error {
"tweakMode": [
"number"
]
}, {
"algorithm": "KAS-KDF",
"mode": "TwoStep",
"revision": "Sp800-56Cr2",
"capabilities": [{
"macSaltMethods": [
"random",
"default"
],
"fixedInfoPattern": "uPartyInfo||vPartyInfo",
"encoding": [
"concatenation"
],
"kdfMode": "feedback",
"macMode": [
"HMAC-SHA2-256"
],
"supportedLengths": [{
"min": 128,
"max": 512,
"increment": 64
}],
"fixedDataOrder": [
"after fixed data"
],
"counterLength": [
8
],
"requiresEmptyIv": true,
"supportsEmptyIv": true
}],
"l": 256,
"z": [256, 384]
}
]`))
}
@ -188,6 +223,29 @@ func doXTS(args [][]byte, decrypt bool) error {
return reply(msg)
}
func hkdfMAC(args [][]byte) error {
if len(args) != 4 {
return fmt.Errorf("HKDF received %d args, wanted 4", len(args))
}
key := args[0]
salt := args[1]
info := args[2]
lengthBytes := args[3]
if len(lengthBytes) != 4 {
return fmt.Errorf("uint32 length was %d bytes long", len(lengthBytes))
}
length := binary.LittleEndian.Uint32(lengthBytes)
mac := hkdf.New(sha256.New, key, salt, info)
ret := make([]byte, length)
mac.Read(ret)
return reply(ret)
}
const (
maxArgs = 8
maxArgLength = 1 << 20

Loading…
Cancel
Save