1// Copyright (c) 2021, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15// testmodulewrapper is a modulewrapper binary that works with acvptool and 16// implements the primitives that BoringSSL's modulewrapper doesn't, so that 17// we have something that can exercise all the code in avcptool. 18 19package main 20 21import ( 22 "bytes" 23 "crypto/aes" 24 "crypto/cipher" 25 "crypto/hmac" 26 "crypto/rand" 27 "crypto/sha256" 28 "encoding/binary" 29 "errors" 30 "fmt" 31 "io" 32 "os" 33 34 "golang.org/x/crypto/hkdf" 35 "golang.org/x/crypto/xts" 36) 37 38var handlers = map[string]func([][]byte) error{ 39 "getConfig": getConfig, 40 "KDF-counter": kdfCounter, 41 "AES-XTS/encrypt": xtsEncrypt, 42 "AES-XTS/decrypt": xtsDecrypt, 43 "HKDF/SHA2-256": hkdfMAC, 44 "hmacDRBG-reseed/SHA2-256": hmacDRBGReseed, 45 "hmacDRBG-pr/SHA2-256": hmacDRBGPredictionResistance, 46 "AES-CBC-CS3/encrypt": ctsEncrypt, 47 "AES-CBC-CS3/decrypt": ctsDecrypt, 48} 49 50func getConfig(args [][]byte) error { 51 if len(args) != 0 { 52 return fmt.Errorf("getConfig received %d args", len(args)) 53 } 54 55 return reply([]byte(`[ 56 { 57 "algorithm": "KDF", 58 "revision": "1.0", 59 "capabilities": [{ 60 "kdfMode": "counter", 61 "macMode": [ 62 "HMAC-SHA2-256" 63 ], 64 "supportedLengths": [{ 65 "min": 8, 66 "max": 4096, 67 "increment": 8 68 }], 69 "fixedDataOrder": [ 70 "before fixed data" 71 ], 72 "counterLength": [ 73 32 74 ] 75 }] 76 }, { 77 "algorithm": "ACVP-AES-XTS", 78 "revision": "1.0", 79 "direction": [ 80 "encrypt", 81 "decrypt" 82 ], 83 "keyLen": [ 84 128, 85 256 86 ], 87 "payloadLen": [ 88 1024 89 ], 90 "tweakMode": [ 91 "number" 92 ] 93 }, { 94 "algorithm": "KAS-KDF", 95 "mode": "TwoStep", 96 "revision": "Sp800-56Cr2", 97 "capabilities": [{ 98 "macSaltMethods": [ 99 "random", 100 "default" 101 ], 102 "fixedInfoPattern": "uPartyInfo||vPartyInfo", 103 "encoding": [ 104 "concatenation" 105 ], 106 "kdfMode": "feedback", 107 "macMode": [ 108 "HMAC-SHA2-256" 109 ], 110 "supportedLengths": [{ 111 "min": 128, 112 "max": 512, 113 "increment": 64 114 }], 115 "fixedDataOrder": [ 116 "after fixed data" 117 ], 118 "counterLength": [ 119 8 120 ], 121 "requiresEmptyIv": true, 122 "supportsEmptyIv": true 123 }], 124 "l": 256, 125 "z": [256, 384] 126 }, { 127 "algorithm": "hmacDRBG", 128 "revision": "1.0", 129 "predResistanceEnabled": [false, true], 130 "reseedImplemented": true, 131 "capabilities": [{ 132 "mode": "SHA2-256", 133 "derFuncEnabled": false, 134 "entropyInputLen": [ 135 256 136 ], 137 "nonceLen": [ 138 128 139 ], 140 "persoStringLen": [ 141 256 142 ], 143 "additionalInputLen": [ 144 256 145 ], 146 "returnedBitsLen": 256 147 }] 148 }, { 149 "algorithm": "ACVP-AES-CBC-CS3", 150 "revision": "1.0", 151 "payloadLen": [{ 152 "min": 128, 153 "max": 2048, 154 "increment": 8 155 }], 156 "direction": [ 157 "encrypt", 158 "decrypt" 159 ], 160 "keyLen": [ 161 128, 162 256 163 ] 164 } 165]`)) 166} 167 168func kdfCounter(args [][]byte) error { 169 if len(args) != 5 { 170 return fmt.Errorf("KDF received %d args", len(args)) 171 } 172 173 outputBytes32, prf, counterLocation, key, counterBits32 := args[0], args[1], args[2], args[3], args[4] 174 outputBytes := binary.LittleEndian.Uint32(outputBytes32) 175 counterBits := binary.LittleEndian.Uint32(counterBits32) 176 177 if !bytes.Equal(prf, []byte("HMAC-SHA2-256")) { 178 return fmt.Errorf("KDF received unsupported PRF %q", string(prf)) 179 } 180 if !bytes.Equal(counterLocation, []byte("before fixed data")) { 181 return fmt.Errorf("KDF received unsupported counter location %q", counterLocation) 182 } 183 if counterBits != 32 { 184 return fmt.Errorf("KDF received unsupported counter length %d", counterBits) 185 } 186 187 if len(key) == 0 { 188 key = make([]byte, 32) 189 rand.Reader.Read(key) 190 } 191 192 // See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf section 5.1 193 if outputBytes+31 < outputBytes { 194 return fmt.Errorf("KDF received excessive output length %d", outputBytes) 195 } 196 197 n := (outputBytes + 31) / 32 198 result := make([]byte, 0, 32*n) 199 mac := hmac.New(sha256.New, key) 200 var input [4 + 8]byte 201 var digest []byte 202 rand.Reader.Read(input[4:]) 203 for i := uint32(1); i <= n; i++ { 204 mac.Reset() 205 binary.BigEndian.PutUint32(input[:4], i) 206 mac.Write(input[:]) 207 digest = mac.Sum(digest[:0]) 208 result = append(result, digest...) 209 } 210 211 return reply(key, input[4:], result[:outputBytes]) 212} 213 214func reply(responses ...[]byte) error { 215 if len(responses) > maxArgs { 216 return fmt.Errorf("%d responses is too many", len(responses)) 217 } 218 219 var lengths [4 * (1 + maxArgs)]byte 220 binary.LittleEndian.PutUint32(lengths[:4], uint32(len(responses))) 221 for i, response := range responses { 222 binary.LittleEndian.PutUint32(lengths[4*(i+1):4*(i+2)], uint32(len(response))) 223 } 224 225 lengthsLength := (1 + len(responses)) * 4 226 if n, err := os.Stdout.Write(lengths[:lengthsLength]); n != lengthsLength || err != nil { 227 return fmt.Errorf("write failed: %s", err) 228 } 229 230 for _, response := range responses { 231 if n, err := os.Stdout.Write(response); n != len(response) || err != nil { 232 return fmt.Errorf("write failed: %s", err) 233 } 234 } 235 236 return nil 237} 238 239func xtsEncrypt(args [][]byte) error { 240 return doXTS(args, false) 241} 242 243func xtsDecrypt(args [][]byte) error { 244 return doXTS(args, true) 245} 246 247func doXTS(args [][]byte, decrypt bool) error { 248 if len(args) != 3 { 249 return fmt.Errorf("XTS received %d args, wanted 3", len(args)) 250 } 251 key := args[0] 252 msg := args[1] 253 tweak := args[2] 254 255 if len(msg)%16 != 0 { 256 return fmt.Errorf("XTS received %d-byte msg, need multiple of 16", len(msg)) 257 } 258 if len(tweak) != 16 { 259 return fmt.Errorf("XTS received %d-byte tweak, wanted 16", len(tweak)) 260 } 261 262 var zeros [8]byte 263 if !bytes.Equal(tweak[8:], zeros[:]) { 264 return errors.New("XTS received tweak with invalid structure. Ensure that configuration specifies a 'number' tweak") 265 } 266 267 sectorNum := binary.LittleEndian.Uint64(tweak[:8]) 268 269 c, err := xts.NewCipher(aes.NewCipher, key) 270 if err != nil { 271 return err 272 } 273 274 if decrypt { 275 c.Decrypt(msg, msg, sectorNum) 276 } else { 277 c.Encrypt(msg, msg, sectorNum) 278 } 279 280 return reply(msg) 281} 282 283func hkdfMAC(args [][]byte) error { 284 if len(args) != 4 { 285 return fmt.Errorf("HKDF received %d args, wanted 4", len(args)) 286 } 287 288 key := args[0] 289 salt := args[1] 290 info := args[2] 291 lengthBytes := args[3] 292 293 if len(lengthBytes) != 4 { 294 return fmt.Errorf("uint32 length was %d bytes long", len(lengthBytes)) 295 } 296 297 length := binary.LittleEndian.Uint32(lengthBytes) 298 299 mac := hkdf.New(sha256.New, key, salt, info) 300 ret := make([]byte, length) 301 mac.Read(ret) 302 303 return reply(ret) 304} 305 306func hmacDRBGReseed(args [][]byte) error { 307 if len(args) != 8 { 308 return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args)) 309 } 310 311 outLenBytes, entropy, personalisation, reseedAdditionalData, reseedEntropy, additionalData1, additionalData2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7] 312 313 if len(outLenBytes) != 4 { 314 return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes)) 315 } 316 outLen := binary.LittleEndian.Uint32(outLenBytes) 317 out := make([]byte, outLen) 318 319 drbg := NewHMACDRBG(entropy, nonce, personalisation) 320 drbg.Reseed(reseedEntropy, reseedAdditionalData) 321 drbg.Generate(out, additionalData1) 322 drbg.Generate(out, additionalData2) 323 324 return reply(out) 325} 326 327func hmacDRBGPredictionResistance(args [][]byte) error { 328 if len(args) != 8 { 329 return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args)) 330 } 331 332 outLenBytes, entropy, personalisation, additionalData1, entropy1, additionalData2, entropy2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7] 333 334 if len(outLenBytes) != 4 { 335 return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes)) 336 } 337 outLen := binary.LittleEndian.Uint32(outLenBytes) 338 out := make([]byte, outLen) 339 340 drbg := NewHMACDRBG(entropy, nonce, personalisation) 341 drbg.Reseed(entropy1, additionalData1) 342 drbg.Generate(out, nil) 343 drbg.Reseed(entropy2, additionalData2) 344 drbg.Generate(out, nil) 345 346 return reply(out) 347} 348 349func swapFinalTwoAESBlocks(d []byte) { 350 var blockNMinus1 [aes.BlockSize]byte 351 copy(blockNMinus1[:], d[len(d)-2*aes.BlockSize:]) 352 copy(d[len(d)-2*aes.BlockSize:], d[len(d)-aes.BlockSize:]) 353 copy(d[len(d)-aes.BlockSize:], blockNMinus1[:]) 354} 355 356func roundUp(n, m int) int { 357 return n + (m-(n%m))%m 358} 359 360func doCTSEncrypt(key, origPlaintext, iv []byte) []byte { 361 // https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38a-add.pdf 362 if len(origPlaintext) < aes.BlockSize { 363 panic("input too small") 364 } 365 366 plaintext := make([]byte, roundUp(len(origPlaintext), aes.BlockSize)) 367 copy(plaintext, origPlaintext) 368 369 block, err := aes.NewCipher(key) 370 if err != nil { 371 panic(err) 372 } 373 cbcEncryptor := cipher.NewCBCEncrypter(block, iv) 374 cbcEncryptor.CryptBlocks(plaintext, plaintext) 375 ciphertext := plaintext 376 377 if len(origPlaintext) > aes.BlockSize { 378 swapFinalTwoAESBlocks(ciphertext) 379 380 if len(origPlaintext)%16 != 0 { 381 // Truncate the ciphertext 382 ciphertext = ciphertext[:len(ciphertext)-aes.BlockSize+(len(origPlaintext)%aes.BlockSize)] 383 } 384 } 385 386 if len(ciphertext) != len(origPlaintext) { 387 panic("internal error") 388 } 389 390 return ciphertext 391} 392 393func doCTSDecrypt(key, origCiphertext, iv []byte) []byte { 394 if len(origCiphertext) < aes.BlockSize { 395 panic("input too small") 396 } 397 398 ciphertext := make([]byte, roundUp(len(origCiphertext), aes.BlockSize)) 399 copy(ciphertext, origCiphertext) 400 401 if len(ciphertext) > aes.BlockSize { 402 swapFinalTwoAESBlocks(ciphertext) 403 } 404 405 block, err := aes.NewCipher(key) 406 if err != nil { 407 panic(err) 408 } 409 cbcDecrypter := cipher.NewCBCDecrypter(block, iv) 410 411 var plaintext []byte 412 if overhang := len(origCiphertext) % aes.BlockSize; overhang == 0 { 413 cbcDecrypter.CryptBlocks(ciphertext, ciphertext) 414 plaintext = ciphertext 415 } else { 416 ciphertext, finalBlock := ciphertext[:len(ciphertext)-aes.BlockSize], ciphertext[len(ciphertext)-aes.BlockSize:] 417 var plaintextFinalBlock [aes.BlockSize]byte 418 block.Decrypt(plaintextFinalBlock[:], finalBlock) 419 copy(ciphertext[len(ciphertext)-aes.BlockSize+overhang:], plaintextFinalBlock[overhang:]) 420 plaintext = make([]byte, len(origCiphertext)) 421 cbcDecrypter.CryptBlocks(plaintext, ciphertext) 422 for i := 0; i < overhang; i++ { 423 plaintextFinalBlock[i] ^= ciphertext[len(ciphertext)-aes.BlockSize+i] 424 } 425 copy(plaintext[len(ciphertext):], plaintextFinalBlock[:overhang]) 426 } 427 428 return plaintext 429} 430 431func ctsEncrypt(args [][]byte) error { 432 if len(args) != 4 { 433 return fmt.Errorf("ctsEncrypt received %d args, wanted 4", len(args)) 434 } 435 436 key, plaintext, iv, numIterations32 := args[0], args[1], args[2], args[3] 437 if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 { 438 return errors.New("only a single iteration supported for ctsEncrypt") 439 } 440 441 if len(plaintext) < aes.BlockSize { 442 return fmt.Errorf("ctsEncrypt plaintext too short: %d bytes", len(plaintext)) 443 } 444 445 return reply(doCTSEncrypt(key, plaintext, iv)) 446} 447 448func ctsDecrypt(args [][]byte) error { 449 if len(args) != 4 { 450 return fmt.Errorf("ctsDecrypt received %d args, wanted 4", len(args)) 451 } 452 453 key, ciphertext, iv, numIterations32 := args[0], args[1], args[2], args[3] 454 if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 { 455 return errors.New("only a single iteration supported for ctsDecrypt") 456 } 457 458 if len(ciphertext) < aes.BlockSize { 459 return errors.New("ctsDecrypt ciphertext too short") 460 } 461 462 return reply(doCTSDecrypt(key, ciphertext, iv)) 463} 464 465const ( 466 maxArgs = 9 467 maxArgLength = 1 << 20 468 maxNameLength = 30 469) 470 471func main() { 472 if err := do(); err != nil { 473 fmt.Fprintf(os.Stderr, "%s.\n", err) 474 os.Exit(1) 475 } 476} 477 478func do() error { 479 var nums [4 * (1 + maxArgs)]byte 480 var argLengths [maxArgs]uint32 481 var args [maxArgs][]byte 482 var argsData []byte 483 484 for { 485 if _, err := io.ReadFull(os.Stdin, nums[:8]); err != nil { 486 return err 487 } 488 489 numArgs := binary.LittleEndian.Uint32(nums[:4]) 490 if numArgs == 0 { 491 return errors.New("Invalid, zero-argument operation requested") 492 } else if numArgs > maxArgs { 493 return fmt.Errorf("Operation requested with %d args, but %d is the limit", numArgs, maxArgs) 494 } 495 496 if numArgs > 1 { 497 if _, err := io.ReadFull(os.Stdin, nums[8:4+4*numArgs]); err != nil { 498 return err 499 } 500 } 501 502 input := nums[4:] 503 var need uint64 504 for i := uint32(0); i < numArgs; i++ { 505 argLength := binary.LittleEndian.Uint32(input[:4]) 506 if i == 0 && argLength > maxNameLength { 507 return fmt.Errorf("Operation with name of length %d exceeded limit of %d", argLength, maxNameLength) 508 } else if argLength > maxArgLength { 509 return fmt.Errorf("Operation with argument of length %d exceeded limit of %d", argLength, maxArgLength) 510 } 511 need += uint64(argLength) 512 argLengths[i] = argLength 513 input = input[4:] 514 } 515 516 if need > uint64(cap(argsData)) { 517 argsData = make([]byte, need) 518 } else { 519 argsData = argsData[:need] 520 } 521 522 if _, err := io.ReadFull(os.Stdin, argsData); err != nil { 523 return err 524 } 525 526 input = argsData 527 for i := uint32(0); i < numArgs; i++ { 528 args[i] = input[:argLengths[i]] 529 input = input[argLengths[i]:] 530 } 531 532 name := string(args[0]) 533 if handler, ok := handlers[name]; !ok { 534 return fmt.Errorf("unknown operation %q", name) 535 } else { 536 if err := handler(args[1:numArgs]); err != nil { 537 return err 538 } 539 } 540 } 541} 542