• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# Program that converts a NIST test vector RSP file to a C header
4# file.  Currently only tested with the ExtendedIV GCM test vectors.
5#
6import argparse
7import itertools
8
9
10def _parse_args():
11  parser = argparse.ArgumentParser()
12  parser.add_argument("-i", "--in", dest="input_files",
13                      help="Comma separated list of input RSP files",
14                      metavar="FILE.rsp", required=True)
15  parser.add_argument("-o", "--out", dest="output_file",
16                      help="Output C header file", metavar="FILE.h",
17                      required=True)
18  return parser.parse_args()
19
20
21def _read_nist_header(lines):
22  """Parse a HEADER block, of form:
23  [A = NUM1]
24  [B = NUM2]
25  \n
26  """
27  header = None  # {KEY1: INT1, KEY2: INT2, ...}
28  while lines:
29    line = lines.pop(0)
30    if not line:
31      return header
32    if line.startswith('['):
33      if not header:
34        header = {}
35      key, value = line.strip('][').split('=')
36      header[key.strip()] = int(value.strip())
37    else:
38      raise Exception('Invalid header block line: %s' % line)
39  return header
40
41
42def _read_nist_blocks(lines):
43  """Parse a DATA block, of form:
44  A = HEX1
45  B = HEX2
46  \n
47  """
48  blocks = []  # [{KEY1: HEXSTR1, KEY2: HEXSTR2, ...}
49  while lines:
50    line = lines[0]
51    if not line:
52      # Block not started, ignore blank line.
53      lines.pop(0)
54      continue
55    if line.startswith('[') or line.startswith('#'):
56      # Next header encountered.
57      break
58    # Read a block.
59    block = {}
60    while lines:
61      line = lines.pop(0)
62      if not line:
63        # End of block.
64        break
65      if '=' not in line:
66        raise Exception('Unexpected line: %s' % line)
67      key, value = line.split('=')
68      block[key.strip()] = value.strip()
69    blocks.append(block)
70  return blocks
71
72
73def _load_nist(infile):
74  lines = infile.readlines()
75  lines = [l.strip() for l in lines]
76  data = []   # [(header1: [block1, block2]), (header2: ...)]
77  mode = lines[1].split(' ')[1]
78
79  while lines:
80    line = lines[0]
81    if not line or line.startswith('#'):
82      # Ignore blank lines or comments.
83      lines.pop(0)
84      continue
85    if not line.startswith('['):
86      raise Exception('Header not found: %s' % line)
87    header = _read_nist_header(lines)
88    blocks = _read_nist_blocks(lines)
89    data.append((header, blocks))
90  return mode, data
91
92
93def _words32(v):
94  # Split hex string into 32-bit words.
95  args = [iter(v)] * 8
96  words = [''.join(b) for b in itertools.izip_longest(fillvalue='', *args)]
97  return map(lambda w: w.ljust(8, '0'), words)    # Zero-pad the last word.
98
99
100def _bswap32(w):
101  if len(w) != 8:
102    raise Exception('Expected 32-bit input word, got: %s' % w)
103  w = w[::-1]   # Reverse hex string.
104  w = iter(w)
105  return ''.join([b2 + b1 for b1, b2 in itertools.izip(w, w)])
106
107
108def _format32(block):
109  # Format values into 32-bit words, with appropriate endienness.
110  b = {}
111  for k, v in block.iteritems():
112    if k == 'Count':
113      b[k] = v
114      continue
115    if not v:    # Strip keys with empty values.
116      continue
117    v = ', '.join(['0x%s' % _bswap32(w) for w in _words32(v)])
118    b[k] = '{%s}' % v
119  return b
120
121
122def _write_header(input_files, outfile, mode, data):
123  outfile.write('/*\n * Auto generated by nist2h.py from input files:\n')
124  for fname in input_files.split(','):
125    outfile.write(' *     %s\n' % fname)
126  outfile.write(' */\n')
127  outfile.write('#ifndef AES_%s_CAVP_H\n' % mode)
128  outfile.write('#define AES_%s_CAVP_H\n' % mode)
129  outfile.write('''
130typedef struct {
131    uint32_t key[8];
132    uint32_t key_len;
133    uint32_t IV[128];
134    uint32_t IV_len;
135    uint32_t PT[64];
136    uint32_t PT_len;
137    uint32_t CT[64];
138    uint32_t AAD[96];
139    uint32_t AAD_len;
140    uint32_t tag[16];
141    uint32_t tag_len;
142} %s_data;
143
144''' % mode.lower())
145  outfile.write('const %s_data NIST_%s_DATA[] = {\n' % (mode.lower(), mode))
146  for i, entry in enumerate(data):
147    header, blocks = entry
148    for block in blocks:
149      block = _format32(block)
150      AAD = '{}'
151      AAD_len = 0
152      if 'AAD' in block:
153        AAD = block['AAD']
154        AAD_len = header['AADlen']
155      PT = '{}'
156      PT_len = 0
157      if 'PT' in block:
158        PT = block['PT']
159        PT_len = header['PTlen']
160      CT = '{}'
161      if 'CT' in block:
162        CT = block['CT']
163
164      line = ('{key}, {key_len}, {IV}, {IV_len}, {PT}, {PT_len}, '
165              '{CT}, {AAD}, {AAD_len}, {tag}, {tag_len}').format(
166                  key=block['Key'], key_len=header['Keylen'], IV=block['IV'],
167                  IV_len=header['IVlen'], PT=PT, PT_len=PT_len, CT=CT, AAD=AAD,
168                  AAD_len=AAD_len, tag=block['Tag'], tag_len=header['Taglen'])
169      outfile.write('    {%s},\n' % line)
170  outfile.write('};\n\n')
171  outfile.write('#endif /* ! AES_TESTS_%s_DATA_H */\n' % mode)
172
173
174if __name__ == '__main__':
175  args = _parse_args()
176  data = []
177  for input_file in args.input_files.split(','):
178    print 'Processing:', input_file
179    with open(input_file) as f:
180      mode, loaded_data = _load_nist(f)
181      data.extend(loaded_data)
182  with open(args.output_file, 'w+') as f:
183    _write_header(args.input_files, f, mode, data)
184