*/
static void
rij_salt_and_iv(RIJNDAEL_context *ctx, const char *key,
- const int key_len, const unsigned char *data)
+ const int key_len, const unsigned char *data, const int legacy_enc_mode)
{
char pw_buf[RIJNDAEL_MAX_KEYSIZE];
- unsigned char tmp_buf[64]; /* How big does this need to be? */
+ unsigned char tmp_buf[MD5_DIGEST_LEN+RIJNDAEL_MAX_KEYSIZE+RIJNDAEL_BLOCKSIZE];
unsigned char kiv_buf[RIJNDAEL_MAX_KEYSIZE+RIJNDAEL_BLOCKSIZE]; /* Key and IV buffer */
unsigned char md5_buf[MD5_DIGEST_LEN]; /* Buffer for computed md5 hash */
- int final_key_len = RIJNDAEL_MIN_KEYSIZE;
+ int final_key_len = 0;
size_t kiv_len = 0;
- /* First make pw 32 bytes (pad with "0" (ascii 0x30)) or truncate.
- * Note: pw_buf was initialized with '0' chars (again, not the value
- * 0, but the digit '0' character).
- */
- if(key_len < RIJNDAEL_MIN_KEYSIZE)
+ if(legacy_enc_mode == 1)
{
- memcpy(pw_buf, key, key_len);
- memset(pw_buf+key_len, '0', RIJNDAEL_MIN_KEYSIZE - key_len);
+ /* First make pw 32 bytes (pad with "0" (ascii 0x30)) or truncate.
+ * Note: pw_buf was initialized with '0' chars (again, not the value
+ * 0, but the digit '0' character).
+ *
+ * This maintains compatibility with the old perl code if absolutely
+ * necessary in some scenarios, but is not recommended to use since it
+ * breaks compatibility with how OpenSSL implements AES. This code
+ * will be removed altogether in a future version of fwknop.
+ */
+ if(key_len < RIJNDAEL_MIN_KEYSIZE)
+ {
+ memcpy(pw_buf, key, key_len);
+ memset(pw_buf+key_len, '0', RIJNDAEL_MIN_KEYSIZE - key_len);
+ final_key_len = RIJNDAEL_MIN_KEYSIZE;
+ }
+ else
+ {
+ memcpy(pw_buf, key, key_len);
+ final_key_len = key_len;
+ }
}
else
{
int encryption_mode)
{
- /* The default (set in fko.h) is ECB mode to be compatible with the
- * Crypt::CBC perl module.
+ /* The default (set in fko.h) is CBC mode
*/
ctx->mode = encryption_mode;
/* Generate the salt and initialization vector.
*/
- rij_salt_and_iv(ctx, key, key_len, data);
+ rij_salt_and_iv(ctx, key, key_len, data, 0);
/* Intialize our Rijndael context.
*/
rijndael_setup(ctx, RIJNDAEL_MAX_KEYSIZE, ctx->key);
}
-/* Take a chunk of data, encrypt it in the same way the perl Crypt::CBC
- * module would.
+/* Take a chunk of data, encrypt it in the same way OpenSSL would
+ * (with a default of AES in CBC mode).
*/
size_t
rij_encrypt(unsigned char *in, size_t in_len,
my $configure_path = 'configure';
my $cmd_out_tmp = 'cmd.out';
my $server_cmd_tmp = 'server_cmd.out';
+my $data_tmp = 'data.tmp';
my $gpg_client_home_dir = "$conf_dir/client-gpg";
my $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw";
my $replay_pcap_file = "$conf_dir/spa_replay.pcap";
my $enable_make_distcheck = 0;
my $enable_perl_module_checks = 0;
my $enable_perl_module_fuzzing_spa_pkt_generation = 0;
+my $enable_openssl_compatibility_tests = 0;
+my $openssl_success_ctr = 0;
+my $openssl_failure_ctr = 0;
+my $openssl_ctr = 0;
my $sudo_path = '';
my $gcov_path = '';
my $killall_path = '';
+my $openssl_path = '';
my $pinentry_fail = 0;
my $platform = '';
my $help = 0;
my $REQUIRE_NO_NEW_REMOVED = 2;
my $MATCH_ANY = 1;
my $MATCH_ALL = 2;
+my $REQUIRE_SUCCESS = 0;
+my $REQUIRE_FAILURE = 1;
my $LINUX = 1;
my $FREEBSD = 2;
my $MACOSX = 3;
'enable-profile-coverage-check' => \$enable_profile_coverage_check,
'enable-ip-resolve' => \$enable_client_ip_resolve_test,
'enable-distcheck' => \$enable_make_distcheck,
+ 'enable-openssl-checks' => \$enable_openssl_compatibility_tests,
'List-mode' => \$list_mode,
'test-limit=i' => \$test_limit,
'enable-valgrind' => \$use_valgrind,
}
}
+if ($enable_openssl_compatibility_tests) {
+ &logr("\n[+] OpenSSL tests passed/failed/executed: " .
+ "$openssl_success_ctr/$openssl_failure_ctr/$openssl_ctr tests\n");
+}
+
if ($use_valgrind) {
&run_test(
{
sub client_send_spa_packet() {
my $test_hr = shift;
+ my $rv = 1;
+
&write_key($default_key, $local_key_file);
- return 0 unless &run_cmd($test_hr->{'cmdline'},
+ $rv = 0 unless &run_cmd($test_hr->{'cmdline'},
$cmd_out_tmp, $curr_test_file);
- return 0 unless &file_find_regex([qr/final\spacked/i],
+ $rv = 0 unless &file_find_regex([qr/final\spacked/i],
$MATCH_ALL, $curr_test_file);
- return 1;
+ if ($enable_openssl_compatibility_tests) {
+
+ ### extract the SPA packet from the cmd tmp file before
+ ### openssl command execution overwrites it
+ my $encoded_msg = '';
+ my $digest = '';
+ my $enc_mode = 0;
+ my $is_hmac_mode = 1;
+ open F, "< $cmd_out_tmp" or die $!;
+ while (<F>) {
+ if (/^\s+Encoded\sData\:\s+(\S+)/) {
+ $encoded_msg = $1;
+ } elsif (/Data\sDigest\:\s(\S+)/) {
+ $digest = $1;
+ } elsif (/Encryption\sMode\:\s+(\d+)/) {
+ $enc_mode = $1;
+ } elsif (/HMAC\:\s\<NULL\>/) {
+ $is_hmac_mode = 0;
+ }
+ }
+ close F;
+
+ $encoded_msg .= ":$digest";
+
+ my $ssl_test_flag = $REQUIRE_SUCCESS;
+ $ssl_test_flag = $REQUIRE_FAILURE if $enc_mode != 2; ### CBC mode
+ $ssl_test_flag = $REQUIRE_FAILURE if $is_hmac_mode;
+
+ my $encrypted_msg = &get_spa_packet_from_file($cmd_out_tmp);
+
+ unless (&openssl_verification($encrypted_msg,
+ $encoded_msg, '', $default_key, $ssl_test_flag)) {
+ $rv = 0;
+ }
+ }
+
+ return $rv;
}
sub permissions_check() {
$fko_obj->destroy();
+ if ($enable_openssl_compatibility_tests) {
+ unless (&openssl_verification($encrypted_msg,
+ '', $msg, $key, $REQUIRE_SUCCESS)) {
+ $rv = 0;
+ }
+ }
+
### now get new object for decryption
$fko_obj = FKO->new();
unless ($fko_obj) {
}
$fko_obj->destroy();
+
+ if ($enable_openssl_compatibility_tests) {
+ unless (&openssl_verification($encrypted_msg,
+ '', $msg, $truncated_key, $REQUIRE_FAILURE)) {
+ $rv = 0;
+ }
+ }
}
&write_test_file("\n", $curr_test_file);
}
}
$fko_obj->destroy();
+
+ if ($enable_openssl_compatibility_tests) {
+ unless (&openssl_verification($encrypted_msg,
+ '', $msg, $key, $REQUIRE_SUCCESS)) {
+ $rv = 0;
+ }
+ }
}
}
}
my $encrypted_msg = $fko_obj->spa_data();
$fko_obj->spa_data($encrypted_msg);
- $fko_obj->decrypt_spa_data($key);
+ $fko_obj->decrypt_spa_data($key, length($key));
if ($msg ne $fko_obj->spa_message()) {
&write_test_file("[-] $msg encrypt/decrypt mismatch\n",
}
$fko_obj->destroy();
+
+ if ($enable_openssl_compatibility_tests) {
+ unless (&openssl_verification($encrypted_msg,
+ '', $msg, $key, $REQUIRE_SUCCESS)) {
+ $rv = 0;
+ }
+ }
}
}
}
}
$fko_obj->spa_data($encrypted_spa_pkt);
- my $status = $fko_obj->decrypt_spa_data($fuzzing_key);
+ my $status = $fko_obj->decrypt_spa_data($fuzzing_key, length($fuzzing_key));
if ($status == FKO->FKO_SUCCESS) {
&write_test_file("[-] Accepted fuzzing $field $field_val SPA packet.\n",
return 0;
}
+sub openssl_verification() {
+ my ($encrypted_msg, $encoded_msg, $access_msg, $key, $rv_flag) = @_;
+ my $rv = 1;
+
+ &write_test_file("[+] OpenSSL verification, (encoded msg: " .
+ "$encoded_msg) (access: $access_msg), key: $key, $encrypted_msg\n",
+ $curr_test_file);
+
+ ### transform encrypted message into the format that openssl expects
+ $encrypted_msg = 'U2FsdGVkX1' . $encrypted_msg
+ unless $encrypted_msg =~ /^U2FsdGVkX1/;
+
+ my $len_remainder = length($encrypted_msg) % 4;
+ if ($len_remainder > 0) {
+ for (my $i=0; $i < 4-$len_remainder; $i++) {
+ $encrypted_msg .= '=';
+ }
+ }
+
+ $encrypted_msg =~ s|(.{76})|$1\n|g;
+
+ open F, "> $data_tmp" or die $!;
+ print F $encrypted_msg, "\n";
+ close F;
+
+ $rv = &run_cmd("$openssl_path enc -d -a -aes-256-cbc " .
+ "-pass pass:$key -in $data_tmp",
+ $cmd_out_tmp, $curr_test_file);
+
+ if ($rv) {
+ if ($rv_flag == $REQUIRE_FAILURE) {
+ &write_test_file("[-] OpenSSL expected decryption failure, " .
+ "but did not get decryption error\n",
+ $curr_test_file);
+ $rv = 0;
+ } else {
+ ### 2868244741993914:dGVzdA:2358972093:2.0.4:1:MS4yLjMANCx0YAAvMjI:vPFqXEA6SnzP2ScsIWAxhg
+
+ ### make sure the access message checks out, or the entire
+ ### decrypted (but not decoded) packet if we were passed the
+ ### encoded version
+ my $decrypted_msg = '';
+ my $decrypted_access_msg = '';
+ my $decoded_msg = '';
+ open F, "< $cmd_out_tmp" or die $!;
+ while (<F>) {
+ if (/^(?:\S+?\:){5}(\S+?)\:/) {
+ $decrypted_access_msg = $1;
+ $decrypted_msg = $_;
+ }
+ }
+ close F;
+
+ $decrypted_msg =~ s/\n//;
+
+ my $decryption_success = 0;
+
+ unless ($encoded_msg) {
+ my $len_remainder = length($decrypted_access_msg) % 4;
+ if ($len_remainder > 0) {
+ for (my $i=0; $i < 4-$len_remainder; $i++) {
+ $decrypted_access_msg .= '=';
+ }
+ }
+ $decoded_msg = decode_base64($decrypted_access_msg);
+ }
+
+ if ($encoded_msg) {
+ $decryption_success = 1 if $encoded_msg eq $decrypted_msg;
+ } else {
+ $decryption_success = 1 if $access_msg eq $decoded_msg;
+ }
+
+ if ($decryption_success) {
+ &write_test_file("[+] OpenSSL access message " .
+ "match in decrypted data\n",
+ $curr_test_file);
+ ### now check the exit status of re-encrypting the data
+ unless (&run_cmd("$openssl_path enc " .
+ "-e -a -aes-256-cbc -pass pass:$key -in $data_tmp",
+ $cmd_out_tmp, $curr_test_file)) {
+
+ &write_test_file("[-] OpenSSL could not re-encrypt\n",
+ $curr_test_file);
+ $rv = 0;
+ }
+ } else {
+ &write_test_file("[-] OpenSSL access message " .
+ "mis-match in decrypted data\n",
+ $curr_test_file);
+ $rv = 0;
+ }
+ }
+ } else {
+ if ($rv_flag == $REQUIRE_SUCCESS) {
+ &write_test_file("[-] OpenSSL bad decryption exit status\n",
+ $curr_test_file);
+ } else {
+ &write_test_file("[+] OpenSSL did not decrypt bogus " .
+ "key/data combination\n",
+ $curr_test_file);
+ }
+ }
+
+ if ($rv) {
+ if ($rv_flag == $REQUIRE_SUCCESS) {
+ &write_test_file("[+] OpenSSL test success (expected " .
+ "encryption/decryption success)\n",
+ $curr_test_file);
+ $openssl_success_ctr++;
+ } else {
+ &write_test_file("[-] OpenSSL test failure (expected " .
+ "encryption/decryption success)\n",
+ $curr_test_file);
+ $openssl_failure_ctr++;
+ $rv = 0;
+ }
+ } else {
+ if ($rv_flag == $REQUIRE_SUCCESS) {
+ &write_test_file("[-] OpenSSL test failure (expected " .
+ "encryption/decryption success)\n",
+ $curr_test_file);
+ $openssl_failure_ctr++;
+ } else {
+ &write_test_file("[+] OpenSSL test success (expected " .
+ "encryption/decryption failure)\n",
+ $curr_test_file);
+ $openssl_success_ctr++;
+ $rv = 1;
+ }
+ }
+ $openssl_ctr++;
+ return $rv;
+}
+
sub specs() {
&run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
close C;
open F, ">> $file" or die "[*] Could not open $file: $!";
- print F $_ for @cmd_lines;
+ for (@cmd_lines) {
+ if (/\n/) {
+ print F $_;
+ } else {
+ print F $_, "\n";
+ }
+ }
close F;
if ($rv == 0) {
$enable_make_distcheck = 1;
$enable_client_ip_resolve_test = 1;
$enable_perl_module_checks = 1;
+ $enable_openssl_compatibility_tests = 1;
+ }
+
+ if ($enable_openssl_compatibility_tests) {
+ $openssl_path = &find_command('openssl');
+ die "[*] openssl command not found." unless $openssl_path;
+ require MIME::Base64;
+ MIME::Base64->import(qw(decode_base64));
}
$enable_perl_module_checks = 1