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