acvp: add XTS support.

Since we don't have XTS in the FIPS module, this change uses
testmodulewrapper for testing.

Change-Id: I82117472ea4288d017983fe9cc11d4ba808a972a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45064
Reviewed-by: David Benjamin <davidben@google.com>
chromium-5359
Adam Langley 4 years ago committed by Adam Langley
parent 595cdc29d6
commit 2f2d27eb5c
  1. 2
      util/fipstools/acvp/ACVP.md
  2. 1
      util/fipstools/acvp/acvptool/subprocess/subprocess.go
  3. 148
      util/fipstools/acvp/acvptool/subprocess/xts.go
  4. BIN
      util/fipstools/acvp/acvptool/test/expected/ACVP-AES-XTS.bz2
  5. 1
      util/fipstools/acvp/acvptool/test/tests.json
  6. BIN
      util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-XTS.bz2
  7. 66
      util/fipstools/acvp/acvptool/testmodulewrapper/testmodulewrapper.go

@ -59,6 +59,8 @@ The other commands are as follows. (Note that you only need to implement the com
| AES-KW/seal | (dummy), key, plaintext, (dummy), (dummy) | Ciphertext |
| AES-KWP/open | (dummy), key, ciphertext, (dummy), (dummy) | One-byte success flag, plaintext or empty |
| AES-KWP/seal | (dummy), key, plaintext, (dummy), (dummy) | Ciphertext |
| AES-XTS/decrypt | Key, ciphertext, tweak | Plaintext |
| AES-XTS/encrypt | Key, plaintext, tweak | Ciphertext |
| AES/decrypt | Key, input block, num iterations¹ | Result, Previous result |
| AES/encrypt | Key, input block, num iterations¹ | Result, Previous result |
| CMAC-AES | Number output bytes, key, message | MAC |

@ -80,6 +80,7 @@ func NewWithIO(cmd *exec.Cmd, in io.WriteCloser, out io.ReadCloser) *Subprocess
"ACVP-AES-ECB": &blockCipher{"AES", 16, 2, true, false, iterateAES},
"ACVP-AES-CBC": &blockCipher{"AES-CBC", 16, 2, true, true, iterateAESCBC},
"ACVP-AES-CTR": &blockCipher{"AES-CTR", 16, 1, false, true, nil},
"ACVP-AES-XTS": &xts{},
"ACVP-TDES-ECB": &blockCipher{"3DES-ECB", 8, 3, true, false, iterate3DES},
"ACVP-TDES-CBC": &blockCipher{"3DES-CBC", 8, 3, true, true, iterate3DESCBC},
"ACVP-AES-GCM": &aead{"AES-GCM", false},

@ -0,0 +1,148 @@
// 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 (
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
)
// The following structures reflect the JSON of ACVP hash tests. See
// https://pages.nist.gov/ACVP/draft-celi-acvp-symmetric.html
type xtsTestVectorSet struct {
Groups []xtsTestGroup `json:"testGroups"`
}
type xtsTestGroup struct {
ID uint64 `json:"tgId"`
Type string `json:"testType"`
Direction string `json:"direction"`
KeyLen int `json:"keyLen"`
PayloadLen int `json:"payloadLen"`
Tests []struct {
ID uint64 `json:"tcId"`
KeyHex string `json:"key"`
PlaintextHex string `json:"pt"`
CiphertextHex string `json:"ct"`
SectorNum *uint64 `json:"sequenceNumber"`
TweakHex *string `json:"tweakValue"`
} `json:"tests"`
}
type xtsTestGroupResponse struct {
ID uint64 `json:"tgId"`
Tests []xtsTestResponse `json:"tests"`
}
type xtsTestResponse struct {
ID uint64 `json:"tcId"`
PlaintextHex string `json:"pt,omitempty"`
CiphertextHex string `json:"ct,omitempty"`
}
// xts implements an ACVP algorithm by making requests to the subprocess to
// encrypt/decrypt with AES-XTS.
type xts struct{}
func (h *xts) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed xtsTestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
}
var ret []xtsTestGroupResponse
for _, group := range parsed.Groups {
response := xtsTestGroupResponse{
ID: group.ID,
}
if group.Type != "AFT" {
return nil, fmt.Errorf("unknown XTS test type %q", group.Type)
}
var decrypt bool
switch group.Direction {
case "encrypt":
decrypt = false
case "decrypt":
decrypt = true
default:
return nil, fmt.Errorf("unknown XTS direction %q", group.Direction)
}
funcName := "AES-XTS/" + group.Direction
for _, test := range group.Tests {
if group.KeyLen != len(test.KeyHex)*4/2 {
return nil, fmt.Errorf("test case %d/%d contains hex message of length %d but specifies a key length of %d (remember that XTS keys are twice the length of the underlying key size)", group.ID, test.ID, len(test.KeyHex), group.KeyLen)
}
key, err := hex.DecodeString(test.KeyHex)
if err != nil {
return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
}
var tweak [16]byte
if test.TweakHex != nil {
t, err := hex.DecodeString(*test.TweakHex)
if err != nil {
return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
}
if len(t) != len(tweak) {
return nil, fmt.Errorf("wrong tweak length (%d bytes) in test case %d/%d", len(t), group.ID, test.ID)
}
copy(tweak[:], t)
} else if test.SectorNum != nil {
// Sector numbers (or "sequence numbers", as NIST calls them) are turned
// into tweak values by encoding them in little-endian form. See IEEE
// 1619-2007, section 5.1.
binary.LittleEndian.PutUint64(tweak[:8], *test.SectorNum)
} else {
return nil, fmt.Errorf("neither sector number nor explicit tweak in test case %d/%d", group.ID, test.ID)
}
var msg []byte
if decrypt {
msg, err = hex.DecodeString(test.CiphertextHex)
} else {
msg, err = hex.DecodeString(test.PlaintextHex)
}
if err != nil {
return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
}
result, err := m.Transact(funcName, 1, key, msg, tweak[:])
if err != nil {
return nil, fmt.Errorf("submodule failed on test case %d/%d: %s", group.ID, test.ID, err)
}
testResponse := xtsTestResponse{ID: test.ID}
if decrypt {
testResponse.PlaintextHex = hex.EncodeToString(result[0])
} else {
testResponse.CiphertextHex = hex.EncodeToString(result[0])
}
response.Tests = append(response.Tests, testResponse)
}
ret = append(ret, response)
}
return ret, nil
}

@ -6,6 +6,7 @@
{"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-GCM.bz2", "Out": "expected/ACVP-AES-GCM.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-KW.bz2", "Out": "expected/ACVP-AES-KW.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-KWP.bz2", "Out": "expected/ACVP-AES-KWP.bz2"},
{"Wrapper": "testmodulewrapper", "In": "vectors/ACVP-AES-XTS.bz2", "Out": "expected/ACVP-AES-XTS.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/ACVP-TDES-CBC.bz2", "Out": "expected/ACVP-TDES-CBC.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/ACVP-TDES-ECB.bz2", "Out": "expected/ACVP-TDES-ECB.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/CMAC-AES.bz2", "Out": "expected/CMAC-AES.bz2"},

@ -6,6 +6,7 @@ package main
import (
"bytes"
"crypto/aes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
@ -14,11 +15,15 @@ import (
"fmt"
"io"
"os"
"golang.org/x/crypto/xts"
)
var handlers = map[string]func([][]byte) error{
"getConfig": getConfig,
"KDF-counter": kdfCounter,
"AES-XTS/encrypt": xtsEncrypt,
"AES-XTS/decrypt": xtsDecrypt,
}
func getConfig(args [][]byte) error {
@ -47,6 +52,23 @@ func getConfig(args [][]byte) error {
32
]
}]
}, {
"algorithm": "ACVP-AES-XTS",
"revision": "1.0",
"direction": [
"encrypt",
"decrypt"
],
"keyLen": [
128,
256
],
"payloadLen": [
1024
],
"tweakMode": [
"number"
]
}
]`))
}
@ -122,6 +144,50 @@ func reply(responses ...[]byte) error {
return nil
}
func xtsEncrypt(args [][]byte) error {
return doXTS(args, false)
}
func xtsDecrypt(args [][]byte) error {
return doXTS(args, true)
}
func doXTS(args [][]byte, decrypt bool) error {
if len(args) != 3 {
return fmt.Errorf("XTS received %d args, wanted 3", len(args))
}
key := args[0]
msg := args[1]
tweak := args[2]
if len(msg)%16 != 0 {
return fmt.Errorf("XTS received %d-byte msg, need multiple of 16", len(msg))
}
if len(tweak) != 16 {
return fmt.Errorf("XTS received %d-byte tweak, wanted 16", len(tweak))
}
var zeros [8]byte
if !bytes.Equal(tweak[8:], zeros[:]) {
return errors.New("XTS received tweak with invalid structure. Ensure that configuration specifies a 'number' tweak")
}
sectorNum := binary.LittleEndian.Uint64(tweak[:8])
c, err := xts.NewCipher(aes.NewCipher, key)
if err != nil {
return err
}
if decrypt {
c.Decrypt(msg, msg, sectorNum)
} else {
c.Encrypt(msg, msg, sectorNum)
}
return reply(msg)
}
const (
maxArgs = 8
maxArgLength = 1 << 20

Loading…
Cancel
Save