android_kernel_xiaomi_sm8450/tools/crypto/gen_fips140_testvecs.py
Eric Biggers 2ee56aad31 ANDROID: fips140: add AES-CMAC
AES-CMAC is a FIPS allowed algorithm, and fips140.ko already has
arm64 implementations of it.  Meanwhile, GKI includes both these arm64
implementations as well as the "cmac" template.  Add the "cmac" template
to fips140.ko too and add a self-test for AES-CMAC, so that we can
include AES-CMAC in the set of algorithms which will be certified.

As with a number of the other algorithms, the criteria for which
algorithms need to be in the certified set are still not particularly
clear, but the latest guidance we've received is to error on the side of
including algorithms.

Bug: 153614920
Bug: 188620248
Change-Id: I6c1d9281fe848a7101d5ef94ab48e5a41bbcc6f8
Signed-off-by: Eric Biggers <ebiggers@google.com>
(cherry picked from commit 038dc9f2cc956cab561bd9d50120920010867b75)
2021-10-29 13:32:13 -07:00

126 lines
4.3 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright 2021 Google LLC
#
# Generate most of the test vectors for the FIPS 140 cryptographic self-tests.
#
# Usage:
# tools/crypto/gen_fips140_testvecs.py > crypto/fips140-generated-testvecs.h
#
# Prerequisites:
# Debian: apt-get install python3-pycryptodome python3-cryptography
# Arch Linux: pacman -S python-pycryptodomex python-cryptography
import hashlib
import hmac
import os
import Cryptodome.Cipher.AES
import Cryptodome.Util.Counter
import cryptography.hazmat.primitives.ciphers
import cryptography.hazmat.primitives.ciphers.algorithms
import cryptography.hazmat.primitives.ciphers.modes
scriptname = os.path.basename(__file__)
message = bytes('This is a 32-byte test message.\0', 'ascii')
aes_key = bytes('128-bit AES key\0', 'ascii')
aes_xts_key = bytes('This is an AES-128-XTS key.\0\0\0\0\0', 'ascii')
aes_iv = bytes('ABCDEFGHIJKLMNOP', 'ascii')
assoc = bytes('associated data string', 'ascii')
hmac_key = bytes('128-bit HMAC key', 'ascii')
def warn_generated():
print(f'''/*
* This header was automatically generated by {scriptname}.
* Don't edit it directly.
*/''')
def is_string_value(value):
return (value.isascii() and
all(c == '\x00' or c.isprintable() for c in str(value, 'ascii')))
def format_value(value, is_string):
if is_string:
return value
hexstr = ''
for byte in value:
hexstr += f'\\x{byte:02x}'
return hexstr
def print_value(name, value):
is_string = is_string_value(value)
hdr = f'static const u8 fips_{name}[{len(value)}] __initconst ='
print(hdr, end='')
if is_string:
value = str(value, 'ascii').rstrip('\x00')
chars_per_byte = 1
else:
chars_per_byte = 4
bytes_per_line = 64 // chars_per_byte
if len(hdr) + (chars_per_byte * len(value)) + 4 <= 80:
print(f' "{format_value(value, is_string)}"', end='')
else:
for chunk in [value[i:i+bytes_per_line]
for i in range(0, len(value), bytes_per_line)]:
print(f'\n\t"{format_value(chunk, is_string)}"', end='')
print(';')
print('')
def generate_aes_testvecs():
print_value('aes_key', aes_key)
print_value('aes_iv', aes_iv)
cbc = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CBC,
iv=aes_iv)
print_value('aes_cbc_ciphertext', cbc.encrypt(message))
ecb = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_ECB)
print_value('aes_ecb_ciphertext', ecb.encrypt(message))
ctr = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_CTR,
nonce=bytes(), initial_value=aes_iv)
print_value('aes_ctr_ciphertext', ctr.encrypt(message))
print_value('aes_gcm_assoc', assoc)
gcm = Cryptodome.Cipher.AES.new(aes_key, Cryptodome.Cipher.AES.MODE_GCM,
nonce=aes_iv[:12], mac_len=16)
gcm.update(assoc)
raw_ciphertext, tag = gcm.encrypt_and_digest(message)
print_value('aes_gcm_ciphertext', raw_ciphertext + tag)
# Unfortunately, pycryptodome doesn't support XTS, so for it we need to use
# a different Python package (the "cryptography" package).
print_value('aes_xts_key', aes_xts_key)
xts = cryptography.hazmat.primitives.ciphers.Cipher(
cryptography.hazmat.primitives.ciphers.algorithms.AES(aes_xts_key),
cryptography.hazmat.primitives.ciphers.modes.XTS(aes_iv)).encryptor()
ciphertext = xts.update(message) + xts.finalize()
print_value('aes_xts_ciphertext', ciphertext)
cmac = Cryptodome.Hash.CMAC.new(aes_key, ciphermod=Cryptodome.Cipher.AES)
cmac.update(message)
print_value('aes_cmac_digest', cmac.digest())
def generate_sha_testvecs():
print_value('hmac_key', hmac_key)
for alg in ['sha1', 'sha256', 'hmac_sha256', 'sha512']:
if alg.startswith('hmac_'):
h = hmac.new(hmac_key, message, alg.removeprefix('hmac_'))
else:
h = hashlib.new(alg, message)
print_value(f'{alg}_digest', h.digest())
print('/* SPDX-License-Identifier: GPL-2.0-only */')
print('/* Copyright 2021 Google LLC */')
print('')
warn_generated()
print('')
print_value('message', message)
generate_aes_testvecs()
generate_sha_testvecs()
warn_generated()