xref: /illumos-gate/usr/src/test/crypto-tests/tests/modes/aes/gmac/nist-gmac-rsp2h.py (revision f51469c0ef9945d3870d6c020b715ae2cb2e09da)
1#! /usr/bin/python
2# This file and its contents are supplied under the terms of the
3# Common Development and Distribution License ("CDDL"), version 1.0.
4# You may only use this file in accordance with the terms of version
5# 1.0 of the CDDL.
6#
7# A full copy of the text of the CDDL should have accompanied this
8# source.  A copy of the CDDL is also available via the Internet at
9# http://www.illumos.org/license/CDDL.
10#
11
12#
13# Copyright 2023 RackTop Systems, Inc.
14#
15
16#
17# Convert an NIST GCMVS test vector file into arrays for crypto-test.
18# Filters for 'plaintextlen' == 0, and uses 'AAD' for DATA arrays,
19# so that the result is a GMAC test.
20#
21
22from dataclasses import dataclass, field
23import argparse
24
25@dataclass(frozen=True)
26class TestVector:
27    """Class tracking individual vectors"""
28
29    key: list
30    iv: list
31    pt: list
32    aad: list
33    ct: list
34    tag: list
35
36@dataclass
37class TestSet:
38    """Class tracking vectors with particular parameters"""
39
40    keylen: int
41    ivlen: int
42    ptlen: int
43    aadlen: int
44    taglen: int
45    ctlen: int = field(init=False)
46    vectors: list = field(default_factory=list)
47
48    def __post_init__(self):
49        self.ctlen = self.ptlen + self.taglen
50
51    def add_case(self, key, iv, pt, aad, ct, tag):
52        if len(key) * 8 != self.keylen:
53            raise ValueError(f'Key \'{" ".join(key)}\' is length {len(key) * 8} but expected length {self.keylen}')
54        if len(iv) * 8 != self.ivlen:
55            raise ValueError(f'Iv \'{" ".join(iv)}\' is length {len(iv) * 8} but expected length {self.ivlen}')
56        if len(pt) * 8 != self.ptlen:
57            raise ValueError(f'Pt \'{" ".join(pt)}\' is length {len(pt) * 8} but expected length {self.ptlen}')
58        if len(aad) * 8 != self.aadlen:
59            raise ValueError(f'Aad \'{" ".join(aad)}\' is length {len(aad) * 8} but expected length {self.aadlen}')
60        if len(ct) * 8 != self.ptlen:
61            raise ValueError(f'Ct \'{" ".join(ct)}\' is length {len(ct) * 8} but expected length {self.ptlen}')
62        if len(tag) * 8 != self.taglen:
63            raise ValueError(f'Tag \'{" ".join(tag)}\' is length {len(tag) * 8} but expected length {self.taglen}')
64        if self.ptlen == 0 and self.taglen == 128 and self.ivlen == 96:
65            #For Decrypt, the tag needs to be part of the ciphertext
66            self.vectors.append(TestVector(key, iv, pt, aad, ct + tag, []))
67
68    def __iter__(self):
69        return iter(self.vectors)
70
71    def __len__(self):
72        return len(self.vectors)
73
74def genhex(string):
75    off = 0
76    strlen = len(string)
77    if strlen % 2 != 0:
78        yield '0x0' + string[0]
79        off += 1
80
81    while off < strlen:
82        yield '0x' + string[off:off+2]
83        off += 2
84
85def main():
86    parser = argparse.ArgumentParser(description=
87        'Convert an NIST GCM Test Vector File to a C header on stdout')
88
89    parser.add_argument('input_file',
90        type=argparse.FileType('r'),
91        help='The NIST .rsp file containing test vectors')
92    args = parser.parse_args()
93
94    input_file = args.input_file
95
96    tests = []
97    tset = None
98    for line in input_file:
99        val = line.strip('[]\n').split(' = ')
100        if not val[0] and tset != None:
101            tset.add_case(key, iv, pt, aad, ct, tag)
102
103        if val[0] == 'Keylen':
104            keylen = int(val[1])
105            tset = None
106        elif val[0] == 'IVlen':
107            ivlen = int(val[1])
108        elif val[0] == 'PTlen':
109            ptlen = int(val[1])
110        elif val[0] == 'AADlen':
111            aadlen = int(val[1])
112        elif val[0] == 'Taglen':
113            taglen = int(val[1])
114        elif val[0] == 'Count':
115            if val[1] == '0':
116                tset = TestSet(keylen, ivlen, ptlen, aadlen, taglen)
117                tests.append(tset)
118        elif val[0] == 'Key':
119            key = list(genhex(val[1]))
120        elif val[0] == 'IV':
121            iv = list(genhex(val[1]))
122        elif val[0] == 'PT':
123            pt = list(genhex(val[1]))
124        elif val[0] == 'AAD':
125            aad = list(genhex(val[1]))
126        elif val[0] == 'CT':
127            ct = list(genhex(val[1]))
128        elif val[0] == 'Tag':
129            tag = list(genhex(val[1]))
130
131
132    def print_hexbuf(buf, buflen):
133        for ind, byte in enumerate(buf):
134            if ind % 8 == 0:
135                print(end='\t')
136            print(f'{byte},', end=' ' if (ind + 1) % 8 != 0 and (ind + 1) != buflen else '\n')
137
138    i = 0
139    datastr = 'uint8_t *DATA[] = {'
140    datalenstr = 'size_t DATALEN[] = {'
141    resstr = 'uint8_t *RES[] = {'
142    reslenstr = 'size_t RESLEN[] = {'
143    ivstr = 'uint8_t *IV[] = {'
144    ivlenstr = 'size_t IVLEN[] = {'
145    keystr = 'uint8_t *KEY[] = {'
146    keylenstr = 'size_t KEYLEN[] = {'
147    for test in tests:
148        for vec in test:
149            print(f'uint8_t GMAC_KEY{i}[] = {{')
150            print_hexbuf(vec.key, test.keylen / 8)
151            print('};\n')
152
153            # For GMAC, there should be no plaintext; use AAD instead
154            if len(vec.pt) != 0:
155                raise ValueError(f'case {i} has plaintext data')
156
157            print(f'uint8_t GMAC_DATA{i}[] = {{')
158            print_hexbuf(vec.aad, test.aadlen / 8)
159            print('};\n')
160
161            print(f'uint8_t GMAC_IV{i}[] = {{')
162            print_hexbuf(vec.iv, test.ivlen / 8 )
163            print('};\n')
164
165            print(f'uint8_t GMAC_RES{i}[] = {{')
166            print_hexbuf(vec.ct, test.ctlen / 8)
167            print('};\n')
168
169            if i % 3 == 0:
170                datalenstr += '\n\t'
171                reslenstr += '\n\t'
172                ivlenstr += '\n\t'
173                keylenstr += '\n\t'
174            elif i != 0:
175                datalenstr += ' '
176                reslenstr += ' '
177                ivlenstr += ' '
178                keylenstr += ' '
179
180            if i % 5 == 0:
181                datastr += '\n\t'
182            elif i != 0:
183                datastr += ' '
184
185            if i % 6 == 0:
186                resstr += '\n\t'
187                ivstr += '\n\t'
188                keystr += '\n\t'
189            elif i != 0:
190                resstr += ' '
191                ivstr += ' '
192                keystr += ' '
193
194            datastr += f'GMAC_DATA{i},'
195            datalenstr += f'sizeof (GMAC_DATA{i}),'
196
197            resstr += f'GMAC_RES{i},'
198            reslenstr += f'sizeof (GMAC_RES{i}),'
199
200            ivstr += f'GMAC_IV{i},'
201            ivlenstr += f'sizeof (GMAC_IV{i}),'
202
203            keystr += f'GMAC_KEY{i},'
204            keylenstr += f'sizeof (GMAC_KEY{i}),'
205
206            i += 1
207
208    print(datastr + '\n};\n')
209    print(datalenstr + '\n};\n')
210    print(resstr + '\n};\n')
211    print(reslenstr + '\n};\n')
212    print(ivstr + '\n};\n')
213    print(ivlenstr + '\n};\n')
214    print(keystr + '\n};\n')
215    print(keylenstr + '\n};\n')
216
217if __name__ == '__main__':
218    try:
219        main()
220    except KeyboardInterrupt:
221        print('Terminated by KeyboardInterrupt.')
222