|  | // Copyright (c) 2019, 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/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 | 
|  | } | 
|  |  | 
|  | 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, m Transactable) (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 = m.Transact(op, 1, key, input, iv) | 
|  | } else { | 
|  | result, err = 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 := 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 := 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 | 
|  | } |