1#! /usr/bin/env perl 2# Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved. 3# 4# Licensed under the Apache License 2.0 (the "License"). You may not use 5# this file except in compliance with the License. You can obtain a copy 6# in the file LICENSE in the source distribution or at 7# https://www.openssl.org/source/license.html 8 9use strict; 10use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/; 11use OpenSSL::Test::Utils; 12use TLSProxy::Proxy; 13 14my $test_name = "test_sslsignature"; 15setup($test_name); 16 17plan skip_all => "TLSProxy isn't usable on $^O" 18 if $^O =~ /^(VMS)$/; 19 20plan skip_all => "$test_name needs the dynamic engine feature enabled" 21 if disabled("engine") || disabled("dynamic-engine"); 22 23plan skip_all => "$test_name needs the sock feature enabled" 24 if disabled("sock"); 25 26plan skip_all => "$test_name needs TLS enabled" 27 if alldisabled(available_protocols("tls")); 28 29$ENV{OPENSSL_ia32cap} = '~0x200000200000000'; 30my $proxy = TLSProxy::Proxy->new( 31 undef, 32 cmdstr(app(["openssl"]), display => 1), 33 srctop_file("apps", "server.pem"), 34 (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) 35); 36 37use constant { 38 NO_CORRUPTION => 0, 39 CORRUPT_SERVER_CERT_VERIFY => 1, 40 CORRUPT_CLIENT_CERT_VERIFY => 2, 41 CORRUPT_TLS1_2_SERVER_KEY_EXCHANGE => 3, 42}; 43 44$proxy->filter(\&signature_filter); 45 46#Test 1: No corruption should succeed 47my $testtype = NO_CORRUPTION; 48$proxy->clientflags("-no_tls1_3") if disabled("ec") && disabled("dh"); 49$proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; 50plan tests => 4; 51ok(TLSProxy::Message->success, "No corruption"); 52 53SKIP: { 54 skip "TLSv1.3 disabled", 1 55 if disabled("tls1_3") || (disabled("ec") && disabled("dh")); 56 57 #Test 2: Corrupting a server CertVerify signature in TLSv1.3 should fail 58 $proxy->clear(); 59 $testtype = CORRUPT_SERVER_CERT_VERIFY; 60 $proxy->start(); 61 ok(TLSProxy::Message->fail, "Corrupt server TLSv1.3 CertVerify"); 62 63 #Test x: Corrupting a client CertVerify signature in TLSv1.3 should fail 64 #$proxy->clear(); 65 #$testtype = CORRUPT_CLIENT_CERT_VERIFY; 66 #$proxy->serverflags("-Verify 5"); 67 #$proxy->clientflags("-cert ".srctop_file("apps", "server.pem")); 68 #$proxy->start(); 69 #ok(TLSProxy::Message->fail, "Corrupt client TLSv1.3 CertVerify"); 70 #TODO(TLS1.3): This test fails due to a problem in s_server/TLSProxy. 71 #Currently a connection is counted as "successful" if the client ends it 72 #with a close_notify. In TLSProxy the client initiates the closure of the 73 #connection so really we should not count it as successful until s_server 74 #has also responded with a close_notify. However s_server never sends a 75 #close_notify - it just closes the connection. Fixing this would be a 76 #significant change to the long established behaviour of s_server. 77 #Unfortunately in this test, it is the server that notices the incorrect 78 #signature and responds with an appropriate alert. However s_client never 79 #sees that because it occurs after the server Finished has been sent. 80 #Therefore s_client just continues to send its application data and sends 81 #its close_notify regardless. TLSProxy sees this and thinks that the 82 #connection was successful when in fact it was not. There isn't an easy fix 83 #for this, so leaving this test commented out for now. 84} 85 86SKIP: { 87 skip "TLS <= 1.2 disabled", 2 88 if alldisabled(("ssl3", "tls1", "tls1_1", "tls1_2")); 89 90 #Test 3: Corrupting a CertVerify signature in <=TLSv1.2 should fail 91 $proxy->clear(); 92 $testtype = CORRUPT_CLIENT_CERT_VERIFY; 93 $proxy->serverflags("-Verify 5"); 94 $proxy->clientflags("-no_tls1_3 -cert ".srctop_file("apps", "server.pem")); 95 $proxy->start(); 96 ok(TLSProxy::Message->fail, "Corrupt <=TLSv1.2 CertVerify"); 97 98 SKIP: { 99 skip "DH disabled", 1 if disabled("dh"); 100 101 #Test 4: Corrupting a ServerKeyExchange signature in <=TLSv1.2 should 102 #fail 103 $proxy->clear(); 104 $testtype = CORRUPT_TLS1_2_SERVER_KEY_EXCHANGE; 105 $proxy->clientflags("-no_tls1_3"); 106 $proxy->cipherc('DHE-RSA-AES128-SHA'); 107 $proxy->ciphers('DHE-RSA-AES128-SHA'); 108 $proxy->start(); 109 ok(TLSProxy::Message->fail, "Corrupt <=TLSv1.2 ServerKeyExchange"); 110 } 111} 112 113sub signature_filter 114{ 115 my $proxy = shift; 116 my $flight; 117 my $mt = TLSProxy::Message::MT_CERTIFICATE_VERIFY; 118 119 if ($testtype == CORRUPT_SERVER_CERT_VERIFY 120 || $testtype == CORRUPT_TLS1_2_SERVER_KEY_EXCHANGE 121 || (!disabled("tls1_3") && $testtype == NO_CORRUPTION)) { 122 $flight = 1; 123 } else { 124 $flight = 2; 125 } 126 127 # We're only interested in the initial server flight 128 return if ($proxy->flight != $flight); 129 130 $mt = TLSProxy::Message::MT_SERVER_KEY_EXCHANGE 131 if ($testtype == CORRUPT_TLS1_2_SERVER_KEY_EXCHANGE); 132 133 foreach my $message (@{$proxy->message_list}) { 134 if ($message->mt == $mt) { 135 my $sig = $message->signature(); 136 my $sigbase = substr($sig, 0, -1); 137 my $sigend = unpack("C", substr($sig, -1)); 138 139 #Flip bits in final byte of signature to corrupt the sig 140 $sigend ^= 0xff unless $testtype == NO_CORRUPTION; 141 142 $message->signature($sigbase.pack("C", $sigend)); 143 $message->repack(); 144 } 145 } 146} 147