154 lines
5.3 KiB
Python
154 lines
5.3 KiB
Python
# Copyright (c) (2020) Apple Inc. All rights reserved.
|
||
#
|
||
# corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
|
||
# is contained in the License.txt file distributed with corecrypto) and only to
|
||
# people who accept that license. IMPORTANT: Any license rights granted to you by
|
||
# Apple Inc. (if any) are limited to internal use within your organization only on
|
||
# devices and computers you own or control, for the sole purpose of verifying the
|
||
# security characteristics and correct functioning of the Apple Software. You may
|
||
# not, directly or indirectly, redistribute the Apple Software or any portions thereof.
|
||
|
||
from cckprng import CCKPRNG
|
||
from copy import deepcopy
|
||
from random import choice
|
||
from codegen import *
|
||
import argparse
|
||
import os
|
||
|
||
SEED_LEN = 32
|
||
NONCE_LEN = 8
|
||
|
||
get_entropy_nsamples = 0
|
||
def fortuna_get_entropy_ones(n):
|
||
global get_entropy_nsamples
|
||
return (get_entropy_nsamples, b"\x01" * n)
|
||
|
||
class TestGenerator(CodeGenerator):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.tid = 0
|
||
self.vecs = []
|
||
|
||
def gentid(self):
|
||
self.tid += 1
|
||
return self.tid
|
||
|
||
def genop_init(self, seed, nonce, getentropy, kprng):
|
||
return self.gendecl('struct cckprng_op_init {}', 'op_init', {
|
||
'hd': {
|
||
'id': self.gentid(),
|
||
'kind': symbol('OP_INIT'),
|
||
},
|
||
'seed': seed,
|
||
'nonce': nonce,
|
||
'getentropy': symbol(getentropy),
|
||
})
|
||
|
||
def genop_reseed(self, seed, nonce, kprng):
|
||
# TKTK: drbg validate_inputs() aborts
|
||
return self.gendecl('struct cckprng_op_reseed {}', 'op_reseed', {
|
||
'hd': {
|
||
'id': self.gentid(),
|
||
'kind': symbol('OP_RESEED'),
|
||
},
|
||
'nonce': uint64(nonce),
|
||
'seed_nbytes': len(seed),
|
||
'seed': seed,
|
||
})
|
||
|
||
def genop_refresh(self, rand, nsamples, kprng):
|
||
return self.gendecl('struct cckprng_op_refresh {}', 'op_refresh', {
|
||
'hd': {
|
||
'id': self.gentid(),
|
||
'kind': symbol('OP_REFRESH'),
|
||
},
|
||
'rand': uint64(rand),
|
||
'nsamples': nsamples,
|
||
'needreseed': kprng.needreseed,
|
||
})
|
||
|
||
def genop_generate(self, rand, kprng):
|
||
return self.gendecl('struct cckprng_op_generate {}', 'op_init', {
|
||
'hd': {
|
||
'id': self.gentid(),
|
||
'kind': symbol('OP_GENERATE'),
|
||
},
|
||
'rand_nbytes': len(rand),
|
||
'out': {
|
||
'rand': rand,
|
||
}
|
||
})
|
||
|
||
def genvector(self, name, note, ops, kprng):
|
||
ops_sym = self.gendecl('struct cckprng_op *{}[]', 'ops', [('const struct cckprng_op *', ref(op)) for op in ops])
|
||
return self.gendecl('struct cckprng_vector {}', name, {
|
||
'id': self.gentid(),
|
||
'note': note,
|
||
'nops': len(ops),
|
||
'ops': ops_sym,
|
||
})
|
||
|
||
def gentest(self, nops):
|
||
getentropy_functions = [(fortuna_get_entropy_ones, "fortuna_get_entropy_ones")]
|
||
getentropy_func, getentropy_name = choice(getentropy_functions)
|
||
|
||
seed = os.urandom(SEED_LEN)
|
||
nonce = os.urandom(NONCE_LEN)
|
||
kprng = CCKPRNG(seed, nonce, getentropy_func)
|
||
ops = [self.genop_init(seed, nonce, getentropy_name, kprng)]
|
||
kinds = ['GENERATE', 'RESEED', 'REFRESH']
|
||
note = None
|
||
|
||
while len(ops) < nops:
|
||
kprngcopy = deepcopy(kprng)
|
||
kind = choice(kinds)
|
||
|
||
try:
|
||
if kind == 'GENERATE':
|
||
rand_nbytes = choice(range(288))
|
||
rand = kprng.generate(rand_nbytes)
|
||
ops.append(self.genop_generate(rand, kprng))
|
||
elif kind == "RESEED":
|
||
seed_nbytes = choice(range(16, 288))
|
||
seed = os.urandom(seed_nbytes)
|
||
nonce = kprng.reseed(seed)
|
||
ops.append(self.genop_reseed(seed, nonce, kprng))
|
||
elif kind == "REFRESH":
|
||
global get_entropy_nsamples
|
||
get_entropy_nsamples = 1024
|
||
success, rand = kprng.refresh()
|
||
ops.append(self.genop_refresh(rand, get_entropy_nsamples, kprng))
|
||
except Exception as e:
|
||
raise Exception(f"Wut? {e}")
|
||
|
||
vec = self.genvector('vec', note, ops, kprng)
|
||
self.vecs.append(vec)
|
||
return vec
|
||
|
||
def finalize(self):
|
||
return self.gendecl('struct cckprng_vector *{}[]', symbol('test_vectors'), [ref(v) for v in self.vecs])
|
||
|
||
def gentests(outfile):
|
||
testgen = TestGenerator()
|
||
for _ in range(256):
|
||
print('generating test', len(testgen.vecs))
|
||
testgen.gentest(16)
|
||
for _ in range(2):
|
||
print('generating test', len(testgen.vecs))
|
||
testgen.gentest(1024)
|
||
testgen.finalize()
|
||
with open(outfile, 'w') as f:
|
||
f.write('\n\n'.join(testgen.decls))
|
||
f.write('\n')
|
||
|
||
if __name__ == "__main__":
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument("command", help = "'generate'")
|
||
parser.add_argument("-o", "--output", help = "C output file")
|
||
args = parser.parse_args()
|
||
|
||
if args.command not in ["generate"]:
|
||
print("Invalid command: %s! Must be 'generate' or 'run'" % args.command)
|
||
sys.exit(-1)
|
||
elif args.command == "generate":
|
||
gentests(args.output) |