summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/ssl_gtest/ssl_keyupdate_unittest.cc
blob: b921d2c1e608b9a35a8d1bfeaabd6d836c39a4a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
#include "sslproto.h"

extern "C" {
// This is not something that should make you happy.
#include "libssl_internals.h"
}

#include "gtest_utils.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

namespace nss_test {

// All stream only tests; DTLS isn't supported yet.

TEST_F(TlsConnectTest, KeyUpdateClient) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
  SendReceive(50);
  SendReceive(60);
  CheckEpochs(4, 3);
}

TEST_F(TlsConnectStreamTls13, KeyUpdateTooEarly_Client) {
  StartConnect();
  auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
      server_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
  filter->EnableDecryption();

  client_->Handshake();
  server_->Handshake();
  ExpectAlert(client_, kTlsAlertUnexpectedMessage);
  client_->Handshake();
  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
  server_->Handshake();
  server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}

TEST_F(TlsConnectStreamTls13, KeyUpdateTooEarly_Server) {
  StartConnect();
  auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
      client_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
  filter->EnableDecryption();

  client_->Handshake();
  server_->Handshake();
  client_->Handshake();
  ExpectAlert(server_, kTlsAlertUnexpectedMessage);
  server_->Handshake();
  server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
  client_->Handshake();
  client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}

TEST_F(TlsConnectTest, KeyUpdateClientRequestUpdate) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
  // SendReceive() only gives each peer one chance to read.  This isn't enough
  // when the read on one side generates another handshake message.  A second
  // read gives each peer an extra chance to consume the KeyUpdate.
  SendReceive(50);
  SendReceive(60);  // Cumulative count.
  CheckEpochs(4, 4);
}

TEST_F(TlsConnectTest, KeyUpdateServer) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
  SendReceive(50);
  SendReceive(60);
  CheckEpochs(3, 4);
}

TEST_F(TlsConnectTest, KeyUpdateServerRequestUpdate) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
  SendReceive(50);
  SendReceive(60);
  CheckEpochs(4, 4);
}

TEST_F(TlsConnectTest, KeyUpdateConsecutiveRequests) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
  SendReceive(50);
  SendReceive(60);
  // The server should have updated twice, but the client should have declined
  // to respond to the second request from the server, since it doesn't send
  // anything in between those two requests.
  CheckEpochs(4, 5);
}

// Check that a local update can be immediately followed by a remotely triggered
// update even if there is no use of the keys.
TEST_F(TlsConnectTest, KeyUpdateLocalUpdateThenConsecutiveRequests) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  // This should trigger an update on the client.
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
  // The client should update for the first request.
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
  // ...but not the second.
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
  SendReceive(50);
  SendReceive(60);
  // Both should have updated twice.
  CheckEpochs(5, 5);
}

TEST_F(TlsConnectTest, KeyUpdateMultiple) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_FALSE));
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
  SendReceive(50);
  SendReceive(60);
  CheckEpochs(5, 6);
}

// Both ask the other for an update, and both should react.
TEST_F(TlsConnectTest, KeyUpdateBothRequest) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  Connect();
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
  EXPECT_EQ(SECSuccess, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
  SendReceive(50);
  SendReceive(60);
  CheckEpochs(5, 5);
}

// If the sequence number exceeds the number of writes before an automatic
// update (currently 3/4 of the max records for the cipher suite), then the
// stack should send an update automatically (but not request one).
TEST_F(TlsConnectTest, KeyUpdateAutomaticOnWrite) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);

  // Set this to one below the write threshold.
  uint64_t threshold = (0x5aULL << 28) * 3 / 4;
  EXPECT_EQ(SECSuccess,
            SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
  EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));

  // This should be OK.
  client_->SendData(10);
  server_->ReadBytes();

  // This should cause the client to update.
  client_->SendData(10);
  server_->ReadBytes();

  SendReceive(100);
  CheckEpochs(4, 3);
}

// If the sequence number exceeds a certain number of reads (currently 7/8 of
// the max records for the cipher suite), then the stack should send AND request
// an update automatically.  However, the sender (client) will be above its
// automatic update threshold, so the KeyUpdate - that it sends with the old
// cipher spec - will exceed the receiver (server) automatic update threshold.
// The receiver gets a packet with a sequence number over its automatic read
// update threshold.  Even though the sender has updated, the code that checks
// the sequence numbers at the receiver doesn't know this and it will request an
// update.  This causes two updates: one from the sender (without requesting a
// response) and one from the receiver (which does request a response).
TEST_F(TlsConnectTest, KeyUpdateAutomaticOnRead) {
  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
  ConnectWithCipherSuite(TLS_AES_128_GCM_SHA256);

  // Move to right at the read threshold.  Unlike the write test, we can't send
  // packets because that would cause the client to update, which would spoil
  // the test.
  uint64_t threshold = ((0x5aULL << 28) * 7 / 8) + 1;
  EXPECT_EQ(SECSuccess,
            SSLInt_AdvanceWriteSeqNum(client_->ssl_fd(), threshold));
  EXPECT_EQ(SECSuccess, SSLInt_AdvanceReadSeqNum(server_->ssl_fd(), threshold));

  // This should cause the client to update, but not early enough to prevent the
  // server from updating also.
  client_->SendData(10);
  server_->ReadBytes();

  // Need two SendReceive() calls to ensure that the update that the server
  // requested is properly generated and consumed.
  SendReceive(70);
  SendReceive(80);
  CheckEpochs(5, 4);
}

}  // namespace nss_test