// 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
inputsAreBlockMultiples bool
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" , "CTR" :
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 b . inputsAreBlockMultiples && 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
}