mirror of
https://git.proxmox.com/git/mirror_zfs
synced 2025-04-28 11:40:17 +00:00
ZTS: ICP encryption tests
This commit adds tests that ensure that the ICP crypto_encrypt() and crypto_decrypt() produce the correct results for all implementations available on this platform. The actual ZTS scripts are simple drivers for the crypto_test program in it's "correctness" mode. This mode takes a file full of test vectors (inputs and expected outputs), runs them, and checks that the results are expected. It will run the tests for each implementation of the algorithm provided by the ICP. The test vectors are taken from Project Wycheproof, which provides a huge number of tests, including exercising many edge cases and common implementation mistakes. These tests are provided are JSON files, so a program is included here to convert them into a simpler line-based format for crypto_test to consume. crypto_test also has a "performance" mode, which will run simple benchmarks against all implementations provded by the ICP and output them for comparison. This is not used by ZTS, but is available to assist with development of new implementations of the underlying primitives. Thanks-to: Joel Low <joel@joelsplace.sg> Sponsored-by: https://despairlabs.com/sponsor/ Signed-off-by: Rob Norris <robn@despairlabs.com> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de> Reviewed-by: Attila Fülöp <attila@fueloep.org>
This commit is contained in:
parent
ece35e0e66
commit
88b0594f93
235
scripts/convert_wycheproof.pl
Executable file
235
scripts/convert_wycheproof.pl
Executable file
@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (c) 2025, Rob Norris <robn@despairlabs.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
#
|
||||
# This programs converts AEAD test vectors from Project Wycheproof into a
|
||||
# format that can be consumed more easily by tests/zfs-tests/cmd/crypto_test.
|
||||
# See tests/zfs-tests/tests/functional/crypto/README for more info.
|
||||
#
|
||||
|
||||
use 5.010;
|
||||
use warnings;
|
||||
use strict;
|
||||
use JSON qw(decode_json);
|
||||
|
||||
sub usage {
|
||||
say "usage: $0 <infile> [<outfile>]";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my ($infile, $outfile) = @ARGV;
|
||||
|
||||
usage() if !defined $infile;
|
||||
|
||||
open my $infh, '<', $infile or die "E: $infile: $!\n";
|
||||
my $json = do { local $/; <$infh> };
|
||||
close $infh;
|
||||
|
||||
my $data = decode_json $json;
|
||||
|
||||
select STDERR;
|
||||
|
||||
# 0.8 had a slightly different format. 0.9* is current, stabilising for 1.0
|
||||
my $version = $data->{generatorVersion} // "[unknown]";
|
||||
if ("$version" !~ m/^0\.9[^0-9]/) {
|
||||
warn
|
||||
"W: this converter was written for Wycheproof 0.9 test vectors\n".
|
||||
" input file has version: $version\n".
|
||||
" bravely continuing, but expect crashes or garbled output\n";
|
||||
}
|
||||
|
||||
# we only support AEAD tests
|
||||
my $schema = $data->{schema} // "[unknown]";
|
||||
if ("$schema" ne 'aead_test_schema.json') {
|
||||
warn
|
||||
"W: this converter is expecting AEAD test vectors\n".
|
||||
" input file has schema: $schema\n".
|
||||
" bravely continuing, but expect crashes or garbled output\n";
|
||||
}
|
||||
|
||||
# sanity check; algorithm is provided
|
||||
my $algorithm = $data->{algorithm};
|
||||
if (!defined $algorithm) {
|
||||
die "E: $infile: required field 'algorithm' not found\n";
|
||||
}
|
||||
|
||||
# sanity check; test count is present and correct
|
||||
my $ntests = 0;
|
||||
$ntests += $_ for map { scalar @{$_->{tests}} } @{$data->{testGroups}};
|
||||
if (!exists $data->{numberOfTests}) {
|
||||
warn "W: input file has no test count, using mine: $ntests\n";
|
||||
} elsif ($data->{numberOfTests} != $ntests) {
|
||||
warn
|
||||
"W: input file has incorrect test count: $data->{numberOfTests}\n".
|
||||
" using my own count: $ntests\n";
|
||||
}
|
||||
|
||||
say " version: $version";
|
||||
say " schema: $schema";
|
||||
say "algorithm: $algorithm";
|
||||
say " ntests: $ntests";
|
||||
|
||||
my $skipped = 0;
|
||||
|
||||
my @tests;
|
||||
|
||||
# tests are grouped into "test groups". groups have the same type and IV, key
|
||||
# and tag sizes. we can infer this info from the tests themselves, but it's
|
||||
# useful for sanity checks
|
||||
#
|
||||
# "testGroups" : [
|
||||
# {
|
||||
# "ivSize" : 96,
|
||||
# "keySize" : 128,
|
||||
# "tagSize" : 128,
|
||||
# "type" : "AeadTest",
|
||||
# "tests" : [ ... ]
|
||||
#
|
||||
for my $group (@{$data->{testGroups}}) {
|
||||
# skip non-AEAD test groups
|
||||
my $type = $group->{type} // "[unknown]";
|
||||
if ($type ne 'AeadTest') {
|
||||
warn "W: group has unexpected type '$type', skipping it\n";
|
||||
$skipped += @{$data->{tests}};
|
||||
next;
|
||||
}
|
||||
|
||||
my ($iv_size, $key_size, $tag_size) =
|
||||
@$group{qw(ivSize keySize tagSize)};
|
||||
|
||||
# a typical test:
|
||||
#
|
||||
# {
|
||||
# "tcId" : 48,
|
||||
# "comment" : "Flipped bit 63 in tag",
|
||||
# "flags" : [
|
||||
# "ModifiedTag"
|
||||
# ],
|
||||
# "key" : "000102030405060708090a0b0c0d0e0f",
|
||||
# "iv" : "505152535455565758595a5b",
|
||||
# "aad" : "",
|
||||
# "msg" : "202122232425262728292a2b2c2d2e2f",
|
||||
# "ct" : "eb156d081ed6b6b55f4612f021d87b39",
|
||||
# "tag" : "d8847dbc326a066988c77ad3863e6083",
|
||||
# "result" : "invalid"
|
||||
# },
|
||||
#
|
||||
# we include everything in the output. the id is useful output so the
|
||||
# user can go back to the original test. comment and flags are useful
|
||||
# for output in a failing test
|
||||
#
|
||||
for my $test (@{$group->{tests}}) {
|
||||
my ($id, $comment, $iv, $key, $msg, $ct, $aad, $tag, $result) =
|
||||
@$test{qw(tcId comment iv key msg ct aad tag result)};
|
||||
|
||||
# sanity check; iv, key and tag must have the length declared
|
||||
# by the group params
|
||||
unless (
|
||||
length_check($id, 'iv', $iv, $iv_size) &&
|
||||
length_check($id, 'key', $key, $key_size) &&
|
||||
length_check($id, 'tag', $tag, $tag_size)) {
|
||||
$skipped++;
|
||||
next;
|
||||
}
|
||||
|
||||
# flatten and sort the flags into a single string
|
||||
my $flags;
|
||||
if ($test->{flags}) {
|
||||
$flags = join(' ', sort @{$test->{flags}});
|
||||
}
|
||||
|
||||
# the completed test record. we'll emit this later once we're
|
||||
# finished with the input; the output file is not open yet.
|
||||
push @tests, [
|
||||
[ id => $id ],
|
||||
[ comment => $comment ],
|
||||
(defined $flags ? [ flags => $flags ] : ()),
|
||||
[ iv => $iv ],
|
||||
[ key => $key ],
|
||||
[ msg => $msg ],
|
||||
[ ct => $ct ],
|
||||
[ aad => $aad ],
|
||||
[ tag => $tag ],
|
||||
[ result => $result ],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($skipped) {
|
||||
$ntests -= $skipped;
|
||||
warn "W: skipped $skipped tests; new test count: $ntests\n";
|
||||
}
|
||||
if ($ntests == 0) {
|
||||
die "E: no tests extracted, sorry!\n";
|
||||
|
||||
|
||||
my $outfh;
|
||||
if ($outfile) {
|
||||
open $outfh, '>', $outfile or die "E: $outfile: $!\n";
|
||||
} else {
|
||||
$outfh = *STDOUT;
|
||||
}
|
||||
|
||||
# the "header" record has the algorithm and count of tests
|
||||
say $outfh "algorithm: $algorithm";
|
||||
say $outfh "tests: $ntests";
|
||||
|
||||
#
|
||||
for my $test (@tests) {
|
||||
# blank line is a record separator
|
||||
say $outfh "";
|
||||
|
||||
# output the test data in a simple record of 'key: value' lines
|
||||
#
|
||||
# id: 48
|
||||
# comment: Flipped bit 63 in tag
|
||||
# flags: ModifiedTag
|
||||
# iv: 505152535455565758595a5b
|
||||
# key: 000102030405060708090a0b0c0d0e0f
|
||||
# msg: 202122232425262728292a2b2c2d2e2f
|
||||
# ct: eb156d081ed6b6b55f4612f021d87b39
|
||||
# aad:
|
||||
# tag: d8847dbc326a066988c77ad3863e6083
|
||||
# result: invalid
|
||||
for my $row (@$test) {
|
||||
my ($k, $v) = @$row;
|
||||
say $outfh "$k: $v";
|
||||
}
|
||||
}
|
||||
|
||||
close $outfh;
|
||||
|
||||
# check that the length of hex string matches the wanted number of bits
|
||||
sub length_check {
|
||||
my ($id, $name, $hexstr, $wantbits) = @_;
|
||||
my $got = length($hexstr)/2;
|
||||
my $want = $wantbits/8;
|
||||
return 1 if $got == $want;
|
||||
my $gotbits = $got*8;
|
||||
say
|
||||
"W: $id: '$name' has incorrect len, skipping test:\n".
|
||||
" got $got bytes ($gotbits bits)\n".
|
||||
" want $want bytes ($wantbits bits)\n";
|
||||
return;
|
||||
}
|
@ -665,6 +665,12 @@ tags = ['functional', 'zap_shrink']
|
||||
tests = ['crtime_001_pos' ]
|
||||
tags = ['functional', 'crtime']
|
||||
|
||||
[tests/functional/crypto]
|
||||
tests = ['icp_aes_ccm', 'icp_aes_gcm']
|
||||
pre =
|
||||
post =
|
||||
tags = ['functional', 'crypto']
|
||||
|
||||
[tests/functional/ctime]
|
||||
tests = ['ctime_001_pos' ]
|
||||
tags = ['functional', 'ctime']
|
||||
|
1
tests/zfs-tests/cmd/.gitignore
vendored
1
tests/zfs-tests/cmd/.gitignore
vendored
@ -4,6 +4,7 @@
|
||||
/clonefile
|
||||
/clone_mmap_cached
|
||||
/clone_mmap_write
|
||||
/crypto_test
|
||||
/devname2devid
|
||||
/dir_rd_update
|
||||
/draid
|
||||
|
@ -30,6 +30,11 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/btree_test
|
||||
libzfs_core.la
|
||||
|
||||
|
||||
scripts_zfs_tests_bin_PROGRAMS += %D%/crypto_test
|
||||
%C%_crypto_test_SOURCES = %D%/crypto_test.c
|
||||
%C%_crypto_test_LDADD = libzpool.la
|
||||
|
||||
|
||||
if WANT_DEVNAME2DEVID
|
||||
scripts_zfs_tests_bin_PROGRAMS += %D%/devname2devid
|
||||
%C%_devname2devid_CFLAGS = $(AM_CFLAGS) $(LIBUDEV_CFLAGS)
|
||||
|
1246
tests/zfs-tests/cmd/crypto_test.c
Normal file
1246
tests/zfs-tests/cmd/crypto_test.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -186,6 +186,7 @@ export ZFSTEST_FILES='badsend
|
||||
clonefile
|
||||
clone_mmap_cached
|
||||
clone_mmap_write
|
||||
crypto_test
|
||||
devname2devid
|
||||
dir_rd_update
|
||||
draid
|
||||
|
@ -139,6 +139,10 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \
|
||||
functional/checksum/default.cfg \
|
||||
functional/clean_mirror/clean_mirror_common.kshlib \
|
||||
functional/clean_mirror/default.cfg \
|
||||
functional/crypto/aes_ccm_test.json \
|
||||
functional/crypto/aes_ccm_test.txt \
|
||||
functional/crypto/aes_gcm_test.json \
|
||||
functional/crypto/aes_gcm_test.txt \
|
||||
functional/cli_root/cli_common.kshlib \
|
||||
functional/cli_root/zfs_copies/zfs_copies.cfg \
|
||||
functional/cli_root/zfs_copies/zfs_copies.kshlib \
|
||||
@ -1427,9 +1431,8 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||
functional/crtime/cleanup.ksh \
|
||||
functional/crtime/crtime_001_pos.ksh \
|
||||
functional/crtime/setup.ksh \
|
||||
functional/ctime/cleanup.ksh \
|
||||
functional/ctime/ctime_001_pos.ksh \
|
||||
functional/ctime/setup.ksh \
|
||||
functional/crypto/icp_aes_ccm.ksh \
|
||||
functional/crypto/icp_aes_gcm.ksh \
|
||||
functional/deadman/deadman_ratelimit.ksh \
|
||||
functional/deadman/deadman_sync.ksh \
|
||||
functional/deadman/deadman_zio.ksh \
|
||||
|
7
tests/zfs-tests/tests/functional/crypto/README
Normal file
7
tests/zfs-tests/tests/functional/crypto/README
Normal file
@ -0,0 +1,7 @@
|
||||
.json test vectors taken from Project Wycheproof:
|
||||
|
||||
https://github.com/c2sp/wycheproof
|
||||
|
||||
Licensed under the Apache License, Version 2.0
|
||||
|
||||
.txt files generated with scripts/convert_wycheproof.pl
|
8602
tests/zfs-tests/tests/functional/crypto/aes_ccm_test.json
Normal file
8602
tests/zfs-tests/tests/functional/crypto/aes_ccm_test.json
Normal file
File diff suppressed because it is too large
Load Diff
6074
tests/zfs-tests/tests/functional/crypto/aes_ccm_test.txt
Normal file
6074
tests/zfs-tests/tests/functional/crypto/aes_ccm_test.txt
Normal file
File diff suppressed because it is too large
Load Diff
4839
tests/zfs-tests/tests/functional/crypto/aes_gcm_test.json
Normal file
4839
tests/zfs-tests/tests/functional/crypto/aes_gcm_test.json
Normal file
File diff suppressed because it is too large
Load Diff
3478
tests/zfs-tests/tests/functional/crypto/aes_gcm_test.txt
Normal file
3478
tests/zfs-tests/tests/functional/crypto/aes_gcm_test.txt
Normal file
File diff suppressed because it is too large
Load Diff
27
tests/zfs-tests/tests/functional/crypto/icp_aes_ccm.ksh
Executable file
27
tests/zfs-tests/tests/functional/crypto/icp_aes_ccm.ksh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/ksh -p
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# This file and its contents are supplied under the terms of the
|
||||
# Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
# You may only use this file in accordance with the terms of version
|
||||
# 1.0 of the CDDL.
|
||||
#
|
||||
# A full copy of the text of the CDDL should have accompanied this
|
||||
# source. A copy of the CDDL is also available via the Internet at
|
||||
# http://www.illumos.org/license/CDDL.
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2025, Rob Norris <robn@despairlabs.com>
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
log_assert "ICP passes test vectors for AES-CCM"
|
||||
|
||||
log_must crypto_test -c $STF_SUITE/tests/functional/crypto/aes_ccm_test.txt
|
||||
|
||||
log_pass "ICP passes test vectors for AES-CCM"
|
27
tests/zfs-tests/tests/functional/crypto/icp_aes_gcm.ksh
Executable file
27
tests/zfs-tests/tests/functional/crypto/icp_aes_gcm.ksh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/ksh -p
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# This file and its contents are supplied under the terms of the
|
||||
# Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
# You may only use this file in accordance with the terms of version
|
||||
# 1.0 of the CDDL.
|
||||
#
|
||||
# A full copy of the text of the CDDL should have accompanied this
|
||||
# source. A copy of the CDDL is also available via the Internet at
|
||||
# http://www.illumos.org/license/CDDL.
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2025, Rob Norris <robn@despairlabs.com>
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
log_assert "ICP passes test vectors for AES-GCM"
|
||||
|
||||
log_must crypto_test -c $STF_SUITE/tests/functional/crypto/aes_gcm_test.txt
|
||||
|
||||
log_pass "ICP passes test vectors for AES-GCM"
|
Loading…
Reference in New Issue
Block a user