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)
126 lines
4.3 KiB
Python
Executable File
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()
|