1#!/usr/bin/env python 2# 3# Copyright (C) 2017 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Outputs CSV data for NIST test cases. 18 19Reads in a selection of test cases in NIST response file format on standard 20input and outputs a CSV representation of them to standard output, with the 21NIST-recommended statement in a header at the top, prefixed by # 22characters. The data is in one of the following formats: 23 24key,iv,plaintext,ciphertext 25key,iv,plaintext,ciphertext,tag,aad""" 26 27import argparse 28import binascii 29import sys 30 31 32def dequote(value): 33 if value[0] == '"' and value[-1] == '"': 34 value = binascii.hexlify(value[1:-1]) 35 return value 36 37 38def format_records(record): 39 # There are a number of different input formats, depending on whether the 40 # particular operation includes an IV, single or multiple cipher operations, 41 # etc. Just check for each possibility. 42 if 'key' in record and 'iv' in record and 'plaintext' in record and 'ciphertext' in record: 43 # A normal operation with an IV 44 return ["{key},{iv},{plaintext},{ciphertext}".format(**record)] 45 elif 'key' in record and 'nonce' in record and 'plaintext' in record and 'ciphertext' in record: 46 # A normal operation with nonce instead of IV 47 return ["{key},{nonce},{plaintext},{ciphertext}".format(**record)] 48 elif 'key' in record and 'plaintext' in record and 'ciphertext' in record: 49 # A normal operation without IV 50 return ["{key},,{plaintext},{ciphertext}".format(**record)] 51 elif 'keys' in record and 'iv' in record and 'plaintext' in record and 'ciphertext' in record: 52 # A single triple-DES operation where all keys are the same 53 return ["{keys}{keys}{keys},{iv},{plaintext},{ciphertext}".format(**record)] 54 elif 'keys' in record and 'plaintext' in record and 'ciphertext' in record: 55 # A single triple-DES operation where all keys are the same without IV 56 return ["{keys}{keys}{keys},,{plaintext},{ciphertext}".format(**record)] 57 elif ('key1' in record and 'key2' in record and 'key3' in record and 'iv' in record 58 and 'plaintext' in record and 'ciphertext' in record): 59 # A single triple-DES operation with different keys for each step 60 return ["{key1}{key2}{key3},{iv},{plaintext},{ciphertext}".format(**record)] 61 elif ('key' in record and 'iv' in record and 'pt' in record and 'aad' in record 62 and 'ct' in record and 'tag' in record): 63 # An AEAD operation 64 return ["{key},{iv},{pt},{ct},{tag},{aad}".format(**record)] 65 elif ('key' in record and 'nonce' in record and 'in' in record and 'ad' in record 66 and 'ct' in record and 'tag' in record): 67 # A BoringSSL AEAD operation 68 return ["{key},{nonce},{in},{ct},{tag},{ad}".format(**record)] 69 return [] 70 71 72def main(): 73 parser = argparse.ArgumentParser() 74 parser.add_argument("--noheader", action='store_true') 75 args = parser.parse_args() 76 if not args.noheader: 77 print """# This data was developed by employees of the National Institute 78# of Standards and Technology (NIST), an agency of the Federal 79# Government. Pursuant to title 17 United States Code Section 105, works 80# of NIST employees are not subject to copyright protection in the United 81# States and are considered to be in the public domain. 82# 83# The data is provided by NIST as a public service and is expressly 84# provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED 85# OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF 86# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT 87# AND DATA ACCURACY. NIST does not warrant or make any representations 88# regarding the use of the data or the results thereof, including but 89# not limited to the correctness, accuracy, reliability or usefulness 90# of the data. NIST SHALL NOT BE LIABLE AND YOU HEREBY RELEASE NIST FROM 91# LIABILITY FOR ANY INDIRECT, CONSEQUENTIAL, SPECIAL, OR INCIDENTAL DAMAGES 92# (INCLUDING DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, 93# LOSS OF BUSINESS INFORMATION, AND THE LIKE), WHETHER ARISING IN TORT, 94# CONTRACT, OR OTHERWISE, ARISING FROM OR RELATING TO THE DATA (OR THE USE 95# OF OR INABILITY TO USE THIS DATA), EVEN IF NIST HAS BEEN ADVISED OF THE 96# POSSIBILITY OF SUCH DAMAGES. 97# 98# To the extent that NIST may hold copyright in countries other than the 99# United States, you are hereby granted the non-exclusive irrevocable 100# and unconditional right to print, publish, prepare derivative works and 101# distribute the NIST data, in any medium, or authorize others to do so 102# on your behalf, on a royalty-free basis throughout the world. 103# 104# You may improve, modify, and create derivative works of the data or any 105# portion of the data, and you may copy and distribute such modifications 106# or works. Modified works should carry a notice stating that you changed 107# the data and should note the date and nature of any such change. Please 108# explicitly acknowledge the National Institute of Standards and Technology 109# as the source of the data: Data citation recommendations are provided 110# below. 111# 112# Permission to use this data is contingent upon your acceptance of 113# the terms of this agreement and upon your providing appropriate 114# acknowledgments of NIST's creation of the data. 115#""" 116 record = {} 117 output_lines = [] 118 for line in sys.stdin.readlines(): 119 line = line.strip() 120 if line == '': 121 output_lines.extend(format_records(record)) 122 record = {} 123 if ' =' in line: 124 record[line[:line.index('=') - 1].lower()] = line[line.index('=') + 2:] 125 if ': ' in line: 126 record[line[:line.index(':')].lower()] = dequote(line[line.index(':') + 2:]) 127 if line == 'FAIL': 128 record['fail'] = True 129 output_lines.extend(format_records(record)) 130 if len(output_lines) > 0: 131 if output_lines[0].count(',') == 3: 132 print """# Data is in the format: 133# key,iv,plaintext,ciphertext""" 134 elif output_lines[0].count(',') == 5: 135 print """# Data is in the format: 136# key,iv,plaintext,ciphertext,tag,aad""" 137 for output_line in output_lines: 138 print output_line 139 140 141 142if __name__ == '__main__': 143 main() 144