summaryrefslogtreecommitdiff
path: root/src/util/fipstools/acvp/acvptool/subprocess/block.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/fipstools/acvp/acvptool/subprocess/block.go')
-rw-r--r--src/util/fipstools/acvp/acvptool/subprocess/block.go252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/util/fipstools/acvp/acvptool/subprocess/block.go b/src/util/fipstools/acvp/acvptool/subprocess/block.go
new file mode 100644
index 00000000..a5ed2cbd
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/subprocess/block.go
@@ -0,0 +1,252 @@
+package subprocess
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+)
+
+// blockCipher implements an ACVP algorithm by making requests to the subprocess
+// to encrypt and decrypt with a block cipher.
+type blockCipher struct {
+ algo string
+ blockSize int
+ hasIV bool
+ m *Subprocess
+}
+
+type blockCipherVectorSet struct {
+ Groups []blockCipherTestGroup `json:"testGroups"`
+}
+
+type blockCipherTestGroup struct {
+ ID uint64 `json:"tgId"`
+ Type string `json:"testType"`
+ Direction string `json:"direction"`
+ KeyBits int `json:"keylen"`
+ Tests []struct {
+ ID uint64 `json:"tcId"`
+ PlaintextHex string `json:"pt"`
+ CiphertextHex string `json:"ct"`
+ IVHex string `json:"iv"`
+ KeyHex string `json:"key"`
+ } `json:"tests"`
+}
+
+type blockCipherTestGroupResponse struct {
+ ID uint64 `json:"tgId"`
+ Tests []blockCipherTestResponse `json:"tests"`
+}
+
+type blockCipherTestResponse struct {
+ ID uint64 `json:"tcId"`
+ CiphertextHex string `json:"ct,omitempty"`
+ PlaintextHex string `json:"pt,omitempty"`
+ MCTResults []blockCipherMCTResult `json:"resultsArray,omitempty"`
+}
+
+type blockCipherMCTResult struct {
+ KeyHex string `json:"key"`
+ PlaintextHex string `json:"pt"`
+ CiphertextHex string `json:"ct"`
+ IVHex string `json:"iv,omitempty"`
+}
+
+func (b *blockCipher) Process(vectorSet []byte) (interface{}, error) {
+ var parsed blockCipherVectorSet
+ if err := json.Unmarshal(vectorSet, &parsed); err != nil {
+ return nil, err
+ }
+
+ var ret []blockCipherTestGroupResponse
+ // See
+ // http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-block-ciph-00.html#rfc.section.5.2
+ // for details about the tests.
+ for _, group := range parsed.Groups {
+ response := blockCipherTestGroupResponse{
+ ID: group.ID,
+ }
+
+ var encrypt bool
+ switch group.Direction {
+ case "encrypt":
+ encrypt = true
+ case "decrypt":
+ encrypt = false
+ default:
+ return nil, fmt.Errorf("test group %d has unknown direction %q", group.ID, group.Direction)
+ }
+
+ op := b.algo + "/encrypt"
+ if !encrypt {
+ op = b.algo + "/decrypt"
+ }
+
+ var mct bool
+ switch group.Type {
+ case "AFT":
+ mct = false
+ case "MCT":
+ mct = true
+ default:
+ return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type)
+ }
+
+ if group.KeyBits%8 != 0 {
+ return nil, fmt.Errorf("test group %d contains non-byte-multiple key length %d", group.ID, group.KeyBits)
+ }
+ keyBytes := group.KeyBits / 8
+
+ for _, test := range group.Tests {
+ if len(test.KeyHex) != keyBytes*2 {
+ return nil, fmt.Errorf("test case %d/%d contains key %q of length %d, but expected %d-bit key", group.ID, test.ID, test.KeyHex, len(test.KeyHex), group.KeyBits)
+ }
+
+ 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 inputHex string
+ if encrypt {
+ inputHex = test.PlaintextHex
+ } else {
+ inputHex = test.CiphertextHex
+ }
+
+ input, err := hex.DecodeString(inputHex)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
+ }
+
+ if len(input)%b.blockSize != 0 {
+ return nil, fmt.Errorf("test case %d/%d has input of length %d, but expected multiple of %d", group.ID, test.ID, len(input), b.blockSize)
+ }
+
+ var iv []byte
+ if b.hasIV {
+ if iv, err = hex.DecodeString(test.IVHex); err != nil {
+ return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
+ }
+ if len(iv) != b.blockSize {
+ return nil, fmt.Errorf("test case %d/%d has IV of length %d, but expected %d", group.ID, test.ID, len(iv), b.blockSize)
+ }
+ }
+
+ testResp := blockCipherTestResponse{ID: test.ID}
+ if !mct {
+ var result [][]byte
+ var err error
+
+ if b.hasIV {
+ result, err = b.m.transact(op, 1, key, input, iv)
+ } else {
+ result, err = b.m.transact(op, 1, key, input)
+ }
+ if err != nil {
+ panic("block operation failed: " + err.Error())
+ }
+
+ if encrypt {
+ testResp.CiphertextHex = hex.EncodeToString(result[0])
+ } else {
+ testResp.PlaintextHex = hex.EncodeToString(result[0])
+ }
+ } else {
+ for i := 0; i < 100; i++ {
+ var iteration blockCipherMCTResult
+ iteration.KeyHex = hex.EncodeToString(key)
+ if encrypt {
+ iteration.PlaintextHex = hex.EncodeToString(input)
+ } else {
+ iteration.CiphertextHex = hex.EncodeToString(input)
+ }
+
+ var result, prevResult []byte
+ if !b.hasIV {
+ for j := 0; j < 1000; j++ {
+ prevResult = input
+ result, err := b.m.transact(op, 1, key, input)
+ if err != nil {
+ panic("block operation failed")
+ }
+ input = result[0]
+ }
+ result = input
+ } else {
+ iteration.IVHex = hex.EncodeToString(iv)
+
+ var prevInput []byte
+ for j := 0; j < 1000; j++ {
+ prevResult = result
+ if j > 0 {
+ if encrypt {
+ iv = result
+ } else {
+ iv = prevInput
+ }
+ }
+
+ results, err := b.m.transact(op, 1, key, input, iv)
+ if err != nil {
+ panic("block operation failed")
+ }
+ result = results[0]
+
+ prevInput = input
+ if j == 0 {
+ input = iv
+ } else {
+ input = prevResult
+ }
+ }
+ }
+
+ if encrypt {
+ iteration.CiphertextHex = hex.EncodeToString(result)
+ } else {
+ iteration.PlaintextHex = hex.EncodeToString(result)
+ }
+
+ switch keyBytes {
+ case 16:
+ for i := range key {
+ key[i] ^= result[i]
+ }
+ case 24:
+ for i := 0; i < 8; i++ {
+ key[i] ^= prevResult[i+8]
+ }
+ for i := range result {
+ key[i+8] ^= result[i]
+ }
+ case 32:
+ for i, b := range prevResult {
+ key[i] ^= b
+ }
+ for i, b := range result {
+ key[i+16] ^= b
+ }
+ default:
+ panic("unhandled key length")
+ }
+
+ if !b.hasIV {
+ input = result
+ } else {
+ iv = result
+ input = prevResult
+ }
+
+ testResp.MCTResults = append(testResp.MCTResults, iteration)
+ }
+ }
+
+ response.Tests = append(response.Tests, testResp)
+ }
+
+ ret = append(ret, response)
+ }
+
+ return ret, nil
+}