2e2a226ac9
Change-Id: Id65e0988534056a72d9b40cc9ba5194e2d9b8a7c Reviewed-on: https://boringssl-review.googlesource.com/15904 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: Adam Langley <agl@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
298 lines
7.6 KiB
Go
298 lines
7.6 KiB
Go
// Copyright (c) 2017, 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.
|
|
|
|
// The make_cavp utility generates cipher_test input files from NIST CAVP Known
|
|
// Answer Test response (.rsp) files.
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
cipher = flag.String("cipher", "", "The name of the cipher/mode (supported: aes, tdes, gcm). Required.")
|
|
cmdLineLabelStr = flag.String("extra-labels", "", "Comma-separated list of additional label pairs to add (e.g. 'Cipher=AES-128-CBC,Operation=ENCRYPT')")
|
|
swapIVAndPlaintext = flag.Bool("swap-iv-plaintext", false, "When processing CBC vector files for CTR mode, swap IV and plaintext.")
|
|
)
|
|
|
|
type kvPair struct {
|
|
key, value string
|
|
}
|
|
|
|
// Test generates a FileTest file from a CAVP response file.
|
|
type Test struct {
|
|
translations map[kvPair]kvPair
|
|
transform func(k, v string) []kvPair
|
|
defaults map[string]string
|
|
// kvDelim is the rune that delimits key-value pairs throughout the file ('=' or ':').
|
|
kvDelim rune
|
|
}
|
|
|
|
func (t *Test) parseKeyValue(s string) (key, value string) {
|
|
if t.kvDelim == 0 {
|
|
i := strings.IndexAny(s, "=:")
|
|
if i != -1 {
|
|
t.kvDelim = rune(s[i])
|
|
}
|
|
}
|
|
if i := strings.IndexRune(s, t.kvDelim); t.kvDelim != 0 && i != -1 {
|
|
key, value = s[:i], s[i+1:]
|
|
if trimmed := strings.TrimSpace(value); len(trimmed) != 0 {
|
|
value = trimmed
|
|
} else {
|
|
value = " "
|
|
}
|
|
} else {
|
|
key = s
|
|
}
|
|
return strings.TrimSpace(key), value
|
|
}
|
|
|
|
func (t *Test) translateKeyValue(key, value string) (string, string) {
|
|
if kv, ok := t.translations[kvPair{key, ""}]; ok {
|
|
if len(kv.value) == 0 && len(value) != 0 {
|
|
return kv.key, value
|
|
}
|
|
return kv.key, kv.value
|
|
}
|
|
if kv, ok := t.translations[kvPair{key, value}]; ok {
|
|
return kv.key, kv.value
|
|
}
|
|
return key, value
|
|
}
|
|
|
|
func printKeyValue(key, value string) {
|
|
if len(value) == 0 {
|
|
fmt.Println(key)
|
|
} else {
|
|
// Omit the value if it is " ", i.e. print "key: ", not "key: ".
|
|
value = strings.TrimSpace(value)
|
|
fmt.Printf("%s: %s\n", key, value)
|
|
}
|
|
}
|
|
|
|
func (t *Test) generate(r io.Reader, cmdLineLabelStr string) {
|
|
s := bufio.NewScanner(r)
|
|
|
|
// Label blocks consist of lines of the form "[key]" or "[key =
|
|
// value]". |labels| holds keys and values of the most recent block
|
|
// of labels.
|
|
var labels map[string]string
|
|
|
|
// Auxiliary labels passed as a flag.
|
|
cmdLineLabels := make(map[string]string)
|
|
if len(cmdLineLabelStr) != 0 {
|
|
pairs := strings.Split(cmdLineLabelStr, ",")
|
|
for _, p := range pairs {
|
|
key, value := t.parseKeyValue(p)
|
|
cmdLineLabels[key] = value
|
|
}
|
|
}
|
|
|
|
t.kvDelim = 0 // Reset kvDelim for scanning the file.
|
|
|
|
// Whether we are in a test or a label section.
|
|
inLabels := false
|
|
inTest := false
|
|
|
|
n := 0
|
|
var currentKv map[string]string
|
|
for s.Scan() {
|
|
n++
|
|
line := s.Text()
|
|
l := strings.TrimSpace(line)
|
|
l = strings.SplitN(l, "#", 2)[0] // Trim trailing comments.
|
|
|
|
// Blank line.
|
|
if len(l) == 0 {
|
|
if inTest {
|
|
// Fill in missing defaults at the end of a test case.
|
|
for k, v := range t.defaults {
|
|
if _, ok := currentKv[k]; !ok {
|
|
printKeyValue(k, v)
|
|
}
|
|
}
|
|
fmt.Println()
|
|
}
|
|
inTest = false
|
|
currentKv = make(map[string]string)
|
|
inLabels = false
|
|
continue
|
|
}
|
|
|
|
// Label section.
|
|
if l[0] == '[' {
|
|
if l[len(l)-1] != ']' {
|
|
log.Fatalf("line #%d invalid: %q", n, line)
|
|
}
|
|
if !inLabels {
|
|
labels = make(map[string]string)
|
|
inLabels = true
|
|
}
|
|
|
|
k, v := t.parseKeyValue(l[1 : len(l)-1])
|
|
k, v = t.translateKeyValue(k, v)
|
|
if len(k) != 0 {
|
|
labels[k] = v
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// Repeat the label map at the beginning of each test section.
|
|
if !inTest {
|
|
inTest = true
|
|
for k, v := range cmdLineLabels {
|
|
printKeyValue(k, v)
|
|
currentKv[k] = v
|
|
}
|
|
for k, v := range labels {
|
|
printKeyValue(k, v)
|
|
currentKv[k] = v
|
|
}
|
|
}
|
|
|
|
// Look up translation and apply transformation (if any).
|
|
k, v := t.parseKeyValue(l)
|
|
k, v = t.translateKeyValue(k, v)
|
|
kvPairs := []kvPair{{k, v}}
|
|
if t.transform != nil {
|
|
kvPairs = t.transform(k, v)
|
|
}
|
|
|
|
for _, kv := range kvPairs {
|
|
k, v := kv.key, kv.value
|
|
if *cipher == "tdes" && k == "Key" {
|
|
v += v + v // Key1=Key2=Key3
|
|
}
|
|
if len(k) != 0 {
|
|
printKeyValue(k, v)
|
|
currentKv[k] = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Fprintln(os.Stderr, "usage: make_cavp <file 1> [<file 2> ...]")
|
|
flag.PrintDefaults()
|
|
}
|
|
|
|
func maybeSwapIVAndPlaintext(k, v string) []kvPair {
|
|
if *swapIVAndPlaintext {
|
|
if k == "Plaintext" {
|
|
return []kvPair{{"IV", v}}
|
|
} else if k == "IV" {
|
|
return []kvPair{{"Plaintext", v}}
|
|
}
|
|
}
|
|
return []kvPair{{k, v}}
|
|
}
|
|
|
|
// Test generator for different values of the -cipher flag.
|
|
var testMap = map[string]Test{
|
|
// Generate cipher_test input file from AESVS .rsp file.
|
|
"aes": Test{
|
|
translations: map[kvPair]kvPair{
|
|
{"ENCRYPT", ""}: {"Operation", "ENCRYPT"},
|
|
{"DECRYPT", ""}: {"Operation", "DECRYPT"},
|
|
{"COUNT", ""}: {"Count", ""},
|
|
{"KEY", ""}: {"Key", ""},
|
|
{"PLAINTEXT", ""}: {"Plaintext", ""},
|
|
{"CIPHERTEXT", ""}: {"Ciphertext", ""},
|
|
{"COUNT", ""}: {"", ""}, // delete
|
|
},
|
|
transform: maybeSwapIVAndPlaintext,
|
|
},
|
|
// Generate cipher_test input file from TMOVS .rsp file.
|
|
"tdes": Test{
|
|
translations: map[kvPair]kvPair{
|
|
{"ENCRYPT", ""}: {"Operation", "ENCRYPT"},
|
|
{"DECRYPT", ""}: {"Operation", "DECRYPT"},
|
|
{"COUNT", ""}: {"Count", ""},
|
|
{"KEYs", ""}: {"Key", ""},
|
|
{"PLAINTEXT", ""}: {"Plaintext", ""},
|
|
{"CIPHERTEXT", ""}: {"Ciphertext", ""},
|
|
{"COUNT", ""}: {"", ""}, // delete
|
|
},
|
|
transform: maybeSwapIVAndPlaintext,
|
|
},
|
|
// Generate aead_test input file from GCMVS .rsp file.
|
|
"gcm": Test{
|
|
translations: map[kvPair]kvPair{
|
|
{"Keylen", ""}: {"", ""}, // delete
|
|
{"IVlen", ""}: {"", ""}, // delete
|
|
{"PTlen", ""}: {"", ""}, // delete
|
|
{"AADlen", ""}: {"", ""}, // delete
|
|
{"Taglen", ""}: {"", ""}, // delete
|
|
{"Count", ""}: {"", ""}, // delete
|
|
{"Key", ""}: {"KEY", ""},
|
|
{"IV", ""}: {"NONCE", ""},
|
|
{"PT", ""}: {"IN", ""},
|
|
{"AAD", ""}: {"AD", ""},
|
|
{"Tag", ""}: {"TAG", ""},
|
|
{"FAIL", ""}: {"FAILS", " "},
|
|
},
|
|
transform: func(k, v string) []kvPair {
|
|
if k == "FAILS" {
|
|
// FAIL cases only appear in the decrypt rsp files. Skip encryption for
|
|
// these.
|
|
return []kvPair{{"FAILS", " "}, {"NO_SEAL", " "}}
|
|
}
|
|
return []kvPair{{k, v}}
|
|
},
|
|
defaults: map[string]string{
|
|
"IN": " ", // FAIL tests don't have IN
|
|
},
|
|
},
|
|
}
|
|
|
|
func main() {
|
|
flag.Usage = usage
|
|
flag.Parse()
|
|
|
|
if len(flag.Args()) == 0 {
|
|
fmt.Fprintf(os.Stderr, "no input files\n\n")
|
|
flag.Usage()
|
|
os.Exit(1)
|
|
}
|
|
|
|
test, ok := testMap[*cipher]
|
|
if !ok {
|
|
fmt.Fprintf(os.Stderr, "invalid cipher: %q\n\n", *cipher)
|
|
flag.Usage()
|
|
os.Exit(1)
|
|
}
|
|
|
|
args := append([]string{"make_cavp"}, os.Args[1:]...)
|
|
fmt.Printf("# Generated by %q\n\n", strings.Join(args, " "))
|
|
|
|
for i, p := range flag.Args() {
|
|
f, err := os.Open(p)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
|
|
fmt.Printf("# File %d: %s\n\n", i+1, p)
|
|
test.generate(f, *cmdLineLabelStr)
|
|
}
|
|
}
|