Merge pull request #10149 from julek-wolfssl/refactor-middle-padding

Refactor record padding handling to eliminate middle padding pattern
This commit is contained in:
David Garske
2026-05-18 16:34:10 -07:00
committed by GitHub
10 changed files with 841 additions and 197 deletions
+27
View File
@@ -138,6 +138,13 @@ jobs:
# This should be a safe limit for the tests to run.
timeout-minutes: 14
steps:
# tlslite-ng is consumed by scripts/multi-msg-record.test (run from
# `make check`); without it that test is SKIPped.
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install tlslite-ng
- name: Build and test wolfSSL
uses: wolfSSL/actions-build-autotools-project@v1
with:
@@ -182,6 +189,13 @@ jobs:
# This should be a safe limit for the tests to run.
timeout-minutes: 14
steps:
# tlslite-ng is consumed by scripts/multi-msg-record.test (run from
# `make check`); without it that test is SKIPped.
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install tlslite-ng
- name: Build and test wolfSSL
uses: wolfSSL/actions-build-autotools-project@v1
with:
@@ -208,6 +222,13 @@ jobs:
# This should be a safe limit for the tests to run.
timeout-minutes: 14
steps:
# tlslite-ng is consumed by scripts/multi-msg-record.test (run from
# `make check`); without it that test is SKIPped.
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install tlslite-ng
- name: Build and test wolfSSL
uses: wolfSSL/actions-build-autotools-project@v1
with:
@@ -270,6 +291,12 @@ jobs:
timeout-minutes: 14
steps:
- uses: actions/checkout@v4
# tlslite-ng is consumed by scripts/multi-msg-record.test (run from
# `make check`); without it that test is SKIPped.
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install tlslite-ng
- run: ./autogen.sh
- name: user_settings_all.h with compatibility layer
run: |
+1
View File
@@ -200,6 +200,7 @@ tracefile.txt
*.bak
*.dummy
*.xcworkspace
workspace.pid*/
xcuserdata
compile
NTRU_algorithm/
+13
View File
@@ -106,9 +106,22 @@ endif
dist_noinst_SCRIPTS+= scripts/unit.test
noinst_SCRIPTS+= scripts/unit.test.in
# multi-msg-record.test drives the wolfSSL example client against a
# Python (tlslite-ng) peer to verify parsing of TLS records that carry
# multiple handshake messages. The script probes the client binary
# at runtime for TLS 1.2, TLS 1.3 and secure-renegotiation support
# and skips phases that are not compiled in. The whole test exits 77
# (SKIP) if python3 or tlslite-ng is missing or if nothing is
# runnable.
dist_noinst_SCRIPTS+= scripts/multi-msg-record.test
endif
endif
# The Python half of multi-msg-record.test always ships in tarballs so
# the wrapper can find it on the installed side.
EXTRA_DIST+= scripts/multi-msg-record.py
dist_noinst_SCRIPTS+= scripts/pem.test
EXTRA_DIST += scripts/sniffer-static-rsa.pcap \
+655
View File
@@ -0,0 +1,655 @@
#!/usr/bin/env python3
#
# multi-msg-record.py
#
# Python half of scripts/multi-msg-record.test (the bash wrapper handles
# NETWORK_UNSHARE_HELPER / AM_BWRAPPED and the python3 availability
# check, then execs this script).
#
# Tests that wolfSSL correctly processes TLS records containing multiple
# handshake messages packed into a single record.
#
# Uses tlslite-ng as the TLS peer to craft multi-message records:
#
# TLS 1.2 Each connection tests TWO code paths back-to-back:
# 1. Initial handshake: RecordMergingSocket rewrites separate
# plaintext ServerHello + Certificate + ServerKeyExchange +
# ServerHelloDone records into one multi-message TLS
# record before forwarding to the wolfSSL client.
# 2. Renegotiation on the same connection: tlslite-ng is
# monkey-patched to coalesce SH+Cert+SKE+SHD into ONE
# encrypted handshake record (exercises the
# curSize -= padSz CBC-padding path and the AEAD path).
#
# TLS 1.3 tlslite-ng's _queue_message / _queue_flush mechanism already
# coalesces EncryptedExtensions + Certificate + CertificateVerify
# + Finished into a single encrypted record. The test verifies
# that wolfSSL parses this correctly.
#
# Multiple cipher suites are tested for both protocol versions.
#
# Requirements: python3, tlslite-ng (pip install tlslite-ng)
import socket
import struct
import subprocess
import os
import sys
import threading
import time
import types
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
WOLFSSL_DIR = os.path.dirname(SCRIPT_DIR)
WOLF_CLIENT = os.path.join(WOLFSSL_DIR, "examples", "client", "client")
CERT_DIR = os.path.join(WOLFSSL_DIR, "certs")
# CA cert path passed to the wolfSSL client via -A. Set in main() after
# detect_wolf_features() determines whether the build accepts PEM or DER.
WOLF_CA_CERT = os.path.join(CERT_DIR, "ca-cert.pem")
# ---------------------------------------------------------------------------
# Bypass a strict tlslite-ng validation that rejects wolfSSL's ClientHello
# when the client advertises FFDHE groups in a TLS-1.3-only hello.
# This must happen before importing TLSConnection.
#
# If tlslite-ng isn't installed we exit 77 so automake marks the test
# SKIPped instead of FAILed.
# ---------------------------------------------------------------------------
try:
import tlslite.tlsconnection # noqa: E402
import tlslite.recordlayer # noqa: E402
tlslite.tlsconnection.TLS_1_3_FORBIDDEN_GROUPS = frozenset()
from tlslite import ( # noqa: E402
TLSConnection, HandshakeSettings, X509CertChain, parsePEMKey,
)
from tlslite.constants import ContentType # noqa: E402
from tlslite.extensions import RenegotiationInfoExtension # noqa: E402
from tlslite.constants import ExtensionType # noqa: E402
from tlslite.messages import HelloMessage, Message as TLSMessage # noqa: E402
except ImportError as e:
sys.stdout.write(
"tlslite-ng not installed ({}); skipping multi-msg-record test\n"
" (install with: pip install tlslite-ng)\n".format(e))
sys.exit(77)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
HS_NAMES = {
2: "SH", 4: "NST", 8: "EE", 11: "Cert", 12: "SKE",
13: "CR", 14: "SHD", 15: "CV", 16: "CKE", 20: "Fin",
}
PASS_COUNT = 0
FAIL_COUNT = 0
SKIP_COUNT = 0
def passed(label):
global PASS_COUNT
PASS_COUNT += 1
print(f" PASS: {label}")
def failed(label):
global FAIL_COUNT
FAIL_COUNT += 1
print(f" FAIL: {label}")
def skipped(label):
global SKIP_COUNT
SKIP_COUNT += 1
print(f" SKIP: {label}")
def detect_wolf_features():
"""Probe the wolfSSL client binary to find which features are
compiled in. Used to decide which test phases to run.
Returns dict with keys: tls12 (bool), tls13 (bool),
secure_reneg (bool), rsa (bool), ciphers (set[str]), ca_cert (str).
"""
feats = {"tls12": False, "tls13": False, "secure_reneg": False,
"rsa": True,
"ciphers": set(),
"ca_cert": os.path.join(CERT_DIR, "ca-cert.pem")}
# ./client -V -> e.g. "3:4:d(downgrade):e(either):"
try:
r = subprocess.run([WOLF_CLIENT, "-V"],
capture_output=True, timeout=5)
parts = r.stdout.decode("utf-8", errors="replace").strip().split(":")
feats["tls12"] = "3" in parts
feats["tls13"] = "4" in parts
except Exception:
pass
# ./client -? -> help text includes "-R" only when
# HAVE_SECURE_RENEGOTIATION is defined. The default -A path
# ("ca-cert.pem" vs "ca-cert.der") also tells us which CA file
# format the build can load. The RSA key-size line reports
# "RSA not supported" when NO_RSA is defined.
try:
r = subprocess.run([WOLF_CLIENT, "-?"],
capture_output=True, timeout=5)
htxt = r.stdout.decode("utf-8", errors="replace")
feats["secure_reneg"] = ("Allow Secure Renegotiation" in htxt)
if "ca-cert.der" in htxt and "ca-cert.pem" not in htxt:
feats["ca_cert"] = os.path.join(CERT_DIR, "ca-cert.der")
if "RSA not supported" in htxt:
feats["rsa"] = False
except Exception:
pass
# ./client -e -> colon-separated list of supported cipher suites.
try:
r = subprocess.run([WOLF_CLIENT, "-e"],
capture_output=True, timeout=5)
ctxt = r.stdout.decode("utf-8", errors="replace").strip()
feats["ciphers"] = {c for c in ctxt.split(":") if c}
except Exception:
pass
return feats
def _load_chain(cert_file):
with open(cert_file) as f:
chain = X509CertChain()
chain.parsePemList(f.read())
return chain
def _load_key(key_file):
with open(key_file) as f:
return parsePEMKey(f.read(), private=True)
def _parse_hs_types(data):
"""Parse handshake message types from raw handshake content."""
msgs = []
off = 0
while off + 4 <= len(data):
ht = data[off]
hl = struct.unpack("!I", b"\x00" + bytes(data[off + 1 : off + 4]))[0]
msgs.append(HS_NAMES.get(ht, f"T{ht}"))
off += 4 + hl
return msgs
def _get_free_port():
"""Get an available TCP port."""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("127.0.0.1", 0))
return s.getsockname()[1]
def _listen_socket():
"""Bind a listening TCP socket on localhost with the standard test timeout."""
port = _get_free_port()
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("127.0.0.1", port))
srv.listen(1)
srv.settimeout(15)
return srv, port
def _run_wolf_client(port, version, cipher, extra=()):
"""Invoke the wolfSSL example client against 127.0.0.1:port.
WOLF_CA_CERT is PEM or DER depending on the build (NO_CODING /
OPENSSL_EXTRA builds don't both support PEM).
"""
cmd = [WOLF_CLIENT, "-h", "127.0.0.1", "-p", str(port),
"-v", version, "-A", WOLF_CA_CERT,
"-g", *extra]
if cipher:
cmd.extend(["-l", cipher])
return subprocess.run(cmd, capture_output=True, timeout=15)
class _SendRecordTrace:
"""Context manager that wraps RecordLayer.sendRecord to log every record."""
def __init__(self):
self.log = []
self._orig = None
def __enter__(self):
self._orig = tlslite.recordlayer.RecordLayer.sendRecord
log = self.log
orig = self._orig
def wrapper(self_rl, msg):
data = msg.write()
ct = msg.contentType
encrypted = bool(self_rl._writeState
and self_rl._writeState.encContext)
hs_msgs = []
if ct == ContentType.handshake:
hs_msgs = _parse_hs_types(data)
log.append((ct, encrypted, len(data), hs_msgs))
yield from orig(self_rl, msg)
tlslite.recordlayer.RecordLayer.sendRecord = wrapper
return self.log
def __exit__(self, *exc):
tlslite.recordlayer.RecordLayer.sendRecord = self._orig
# ---------------------------------------------------------------------------
# RecordMergingSocket (TLS 1.2 plaintext record merging)
# ---------------------------------------------------------------------------
class RecordMergingSocket:
"""Socket wrapper that rewrites consecutive TLS handshake records into
a single multi-message record. Only merges plaintext records that
precede ChangeCipherSpec."""
def __init__(self, sock):
self._sock = sock
self._pending = bytearray()
self._ver = 0x0303
self._after_ccs = False
self.merged_msgs = [] # [(n_msgs, [names], size)]
def _flush(self):
if not self._pending:
return
msgs = _parse_hs_types(self._pending)
n = len(msgs)
hdr = struct.pack("!BHH", 22, self._ver, len(self._pending))
self._sock.sendall(hdr + bytes(self._pending))
self.merged_msgs.append((n, msgs, len(self._pending)))
self._pending = bytearray()
# Called by BufferedSocket (one record per call, or multiple from flush)
def _process(self, data):
data = bytearray(data)
off = 0
while off + 5 <= len(data):
ct = data[off]
ver = struct.unpack("!H", data[off + 1 : off + 3])[0]
rlen = struct.unpack("!H", data[off + 3 : off + 5])[0]
if off + 5 + rlen > len(data):
break
payload = data[off + 5 : off + 5 + rlen]
if not self._after_ccs and ct == 22:
self._pending.extend(payload)
self._ver = ver
else:
if ct == 20:
self._after_ccs = True
self._flush()
self._sock.sendall(bytes(data[off : off + 5 + rlen]))
off += 5 + rlen
def send(self, data):
self._process(data)
return len(data)
def sendall(self, data):
self._process(data)
def recv(self, bufsize):
self._flush()
return self._sock.recv(bufsize)
def __getattr__(self, name):
return getattr(self._sock, name)
# ---------------------------------------------------------------------------
# Test runners
# ---------------------------------------------------------------------------
def run_tls12_test(cipher_wolf, cert_chain, priv_key, label,
do_reneg=True):
"""TLS 1.2 test one connection optionally exercises two code paths:
Phase 1 (plaintext grouping, initial handshake):
RecordMergingSocket rewrites separate plaintext ServerHello,
Certificate, ServerKeyExchange and ServerHelloDone records into
one multi-message TLS record before delivery to wolfSSL.
Phase 2 (encrypted grouping, renegotiation on same connection):
tlslite-ng server is monkey-patched to coalesce SH+Cert+SKE+SHD
into a single encrypted handshake record inside the renegotiation
(exercises wolfSSL's encrypted multi-message parsing including
curSize -= padSz for CBC padding).
Phase 2 is skipped when do_reneg=False (e.g. the wolfSSL client was
built without HAVE_SECURE_RENEGOTIATION).
"""
srv, port = _listen_socket()
result = {"ok": False, "error": ""}
msock_ref = [None]
trace_log = []
reneg_active = [False]
verify_data = {'client': None, 'server': None}
# --- monkey-patches (used only during this connection) ----------------
orig_calc_key = tlslite.tlsconnection.calc_key
def capturing_calc_key(*args, **kwargs):
res = orig_calc_key(*args, **kwargs)
lbl = args[3] if len(args) > 3 else kwargs.get('label', b'')
if lbl == b"client finished" and verify_data['client'] is None:
verify_data['client'] = bytearray(res)
elif lbl == b"server finished" and verify_data['server'] is None:
verify_data['server'] = bytearray(res)
return res
orig_getExt = HelloMessage.getExtension
def patched_getExt(self, ext_type):
ext = orig_getExt(self, ext_type)
if (ext_type == ExtensionType.renegotiation_info
and ext is not None and reneg_active[0]):
ext._internal_value = bytearray(0)
return ext
orig_rie_create = RenegotiationInfoExtension.create
def patched_rie_create(self, data):
if reneg_active[0] and data == bytearray(0):
combined = (bytearray(verify_data['client'])
+ bytearray(verify_data['server']))
return orig_rie_create(self, combined)
return orig_rie_create(self, data)
# ----------------------------------------------------------------------
def server():
try:
tlslite.tlsconnection.calc_key = capturing_calc_key
HelloMessage.getExtension = patched_getExt
RenegotiationInfoExtension.create = patched_rie_create
conn, _ = srv.accept()
conn.settimeout(15)
msock = RecordMergingSocket(conn)
msock_ref[0] = msock
tls = TLSConnection(msock)
settings = HandshakeSettings()
settings.minVersion = (3, 3)
settings.maxVersion = (3, 3)
# ---------- Phase 1: initial handshake (plaintext grouping) ----
tls.handshakeServer(certChain=cert_chain, privateKey=priv_key,
settings=settings)
tlslite.tlsconnection.calc_key = orig_calc_key
data = tls.recv(4096)
if do_reneg:
# ---------- Phase 2: trigger + run renegotiation ----------
hr = TLSMessage(ContentType.handshake,
bytearray([0, 0, 0, 0]))
for _ in tls._sendMsg(hr, randomizeFirstBlock=False,
update_hashes=False):
pass
# Bypass tlslite-ng renegotiation guards
tls.closed = True
tls.session = None
reneg_active[0] = True
# Coalesce handshake messages into ONE encrypted TLS record
def coalescing_sendMsgs(self, msgs):
for msg in msgs:
self._queue_message(msg)
yield from self._queue_flush()
tls._sendMsgs = types.MethodType(coalescing_sendMsgs, tls)
with _SendRecordTrace() as log:
tls.handshakeServer(certChain=cert_chain,
privateKey=priv_key,
settings=settings)
reneg_active[0] = False
trace_log.extend(log)
if data:
tls.send(data)
tls.close()
result["ok"] = True
except Exception as e:
import traceback
result["error"] = traceback.format_exc()
finally:
tlslite.tlsconnection.calc_key = orig_calc_key
HelloMessage.getExtension = orig_getExt
RenegotiationInfoExtension.create = orig_rie_create
reneg_active[0] = False
srv.close()
st = threading.Thread(target=server, daemon=True)
st.start()
time.sleep(0.1)
proc = _run_wolf_client(port, "3", cipher_wolf,
extra=("-R",) if do_reneg else ())
st.join(timeout=5)
if proc.returncode != 0 or not result["ok"]:
err = (result["error"]
or proc.stderr.decode("utf-8", errors="replace")[:400])
failed(f"{label}: connection failed ({err})")
return False
ok = True
# Phase 1 verification: plaintext multi-message record
msock = msock_ref[0]
has_pt_grouped = False
for n, msgs, sz in (msock.merged_msgs if msock else []):
if n > 1:
has_pt_grouped = True
passed(f"{label} [plaintext]: {n} msgs "
f"[{'+'.join(msgs)}] in one record ({sz} bytes)")
if not has_pt_grouped:
failed(f"{label} [plaintext]: no multi-message record detected")
ok = False
# Phase 2 verification: encrypted multi-message record (renego)
if do_reneg:
has_enc_grouped = False
for ct, enc, sz, msgs in trace_log:
if ct == ContentType.handshake and enc and len(msgs) > 1:
has_enc_grouped = True
passed(f"{label} [encrypted]: {len(msgs)} msgs "
f"[{'+'.join(msgs)}] in one record ({sz} bytes)")
if not has_enc_grouped:
failed(f"{label} [encrypted]: no multi-message "
f"encrypted record")
ok = False
return ok
def run_tls13_test(cipher_wolf, cert_chain, priv_key, label):
"""TLS 1.3: verify tlslite-ng sends multi-msg encrypted record and
wolfSSL client processes it."""
srv, port = _listen_socket()
result = {"ok": False, "error": ""}
def server():
try:
conn, _ = srv.accept()
conn.settimeout(15)
tls = TLSConnection(conn)
settings = HandshakeSettings()
settings.minVersion = (3, 4)
settings.maxVersion = (3, 4)
tls.handshakeServer(certChain=cert_chain, privateKey=priv_key,
settings=settings)
data = tls.recv(4096)
if data:
tls.send(data)
tls.close()
result["ok"] = True
except Exception as e:
result["error"] = str(e)
finally:
srv.close()
with _SendRecordTrace() as log:
st = threading.Thread(target=server, daemon=True)
st.start()
time.sleep(0.1)
proc = _run_wolf_client(port, "4", cipher_wolf)
st.join(timeout=5)
if proc.returncode != 0 or not result["ok"]:
err = result["error"] or proc.stderr.decode("utf-8", errors="replace")[:200]
failed(f"{label}: handshake failed ({err})")
return False
# Check that at least one encrypted handshake record has multiple messages
has_multi = False
for ct, enc, sz, msgs in log:
if ct == ContentType.handshake and enc and len(msgs) > 1:
has_multi = True
passed(f"{label}: {len(msgs)} encrypted msgs "
f"[{'+'.join(msgs)}] in one record ({sz} bytes)")
if not has_multi:
failed(f"{label}: no multi-message encrypted records")
return False
return True
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
if not os.path.isfile(WOLF_CLIENT):
print(f"ERROR: wolfSSL client not found: {WOLF_CLIENT}")
print(" Build wolfSSL first (./configure && make)")
sys.exit(1)
# Probe the client to see which features are compiled in so each
# phase of the test is only run when it can succeed.
feats = detect_wolf_features()
global WOLF_CA_CERT
WOLF_CA_CERT = feats["ca_cert"]
print("=" * 60)
print(" Multi-Message TLS Record Test")
print("=" * 60)
print(f" wolfSSL features: TLS1.2={feats['tls12']} "
f"TLS1.3={feats['tls13']} "
f"secure_reneg={feats['secure_reneg']} "
f"rsa={feats['rsa']}")
# The test certs are RSA; skip the whole test when the wolfSSL build
# has no RSA support (the client can't load or verify them).
if not feats["rsa"]:
print("\n wolfSSL built without RSA; skipping multi-msg-record "
"test (RSA test certs cannot be verified).")
sys.exit(77)
# Load certificate / key pairs
rsa_chain = _load_chain(os.path.join(CERT_DIR, "server-cert.pem"))
rsa_key = _load_key(os.path.join(CERT_DIR, "server-key.pem"))
# ------------------------------------------------------------------
# TLS 1.2 plaintext (initial HS) + optional encrypted (renegotiation)
# multi-message records, same connection per cipher suite.
# ------------------------------------------------------------------
tls12_suites = [
# (wolfSSL cipher name, description)
(None, "default negotiated"),
# AEAD (GCM)
("ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA AES128-GCM"),
("ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA AES256-GCM"),
("DHE-RSA-AES128-GCM-SHA256", "DHE-RSA AES128-GCM"),
("DHE-RSA-AES256-GCM-SHA384", "DHE-RSA AES256-GCM"),
# CBC + HMAC (exercises padding path)
("ECDHE-RSA-AES128-SHA256", "ECDHE-RSA AES128-CBC-SHA256"),
("ECDHE-RSA-AES256-SHA384", "ECDHE-RSA AES256-CBC-SHA384"),
("DHE-RSA-AES128-SHA256", "DHE-RSA AES128-CBC-SHA256"),
("DHE-RSA-AES256-SHA256", "DHE-RSA AES256-CBC-SHA256"),
# AEAD (ChaCha20-Poly1305)
("ECDHE-RSA-CHACHA20-POLY1305", "ECDHE-RSA CHACHA20-POLY1305"),
("DHE-RSA-CHACHA20-POLY1305", "DHE-RSA CHACHA20-POLY1305"),
]
if feats["tls12"]:
if feats["secure_reneg"]:
print("\n--- TLS 1.2: plaintext + encrypted multi-message "
"records ---")
print(" Each connection verifies BOTH code paths:")
print(" * initial handshake -> plaintext SH+Cert+SKE+SHD")
print(" * renegotiation -> encrypted SH+Cert+SKE+SHD")
else:
print("\n--- TLS 1.2: plaintext multi-message records ---")
print(" wolfSSL built without HAVE_SECURE_RENEGOTIATION;")
print(" skipping the encrypted (renegotiation) half.")
print(" Covers multiple key-exchanges, ciphers and MAC "
"families.\n")
for cipher, desc in tls12_suites:
if cipher and cipher not in feats["ciphers"]:
skipped(f"TLS1.2 {desc} - cipher not in wolfSSL build")
continue
run_tls12_test(cipher, rsa_chain, rsa_key,
f"TLS1.2 {desc}",
do_reneg=feats["secure_reneg"])
if not feats["secure_reneg"]:
skipped("TLS1.2 encrypted multi-msg record "
"(requires HAVE_SECURE_RENEGOTIATION)")
else:
skipped(f"TLS 1.2 tests ({len(tls12_suites)} suites) - "
"wolfSSL built without TLS 1.2")
# ------------------------------------------------------------------
# TLS 1.3 encrypted multi-message records
# ------------------------------------------------------------------
tls13_suites = [
# (wolfSSL cipher name, description)
(None, "default negotiated"),
("TLS13-AES128-GCM-SHA256", "AES-128-GCM"),
("TLS13-AES256-GCM-SHA384", "AES-256-GCM"),
("TLS13-CHACHA20-POLY1305-SHA256", "CHACHA20-POLY1305"),
]
if feats["tls13"]:
print("\n--- TLS 1.3: encrypted multi-message records ---")
print(" Server sends EE+Cert+CV+Fin in a single encrypted "
"record;")
print(" wolfSSL client must decrypt and parse.\n")
for cipher, desc in tls13_suites:
if cipher and cipher not in feats["ciphers"]:
skipped(f"TLS1.3 {desc} - cipher not in wolfSSL build")
continue
run_tls13_test(cipher, rsa_chain, rsa_key,
f"TLS1.3 {desc}")
else:
skipped(f"TLS 1.3 tests ({len(tls13_suites)} suites) - "
"wolfSSL built without TLS 1.3")
# ------------------------------------------------------------------
# Summary
# ------------------------------------------------------------------
print()
print("=" * 60)
print(f" Results: {PASS_COUNT} passed, {FAIL_COUNT} failed, "
f"{SKIP_COUNT} skipped")
print("=" * 60)
# If nothing at all could run, signal SKIP (exit 77) so automake
# records the test as skipped rather than passed-with-nothing.
if PASS_COUNT == 0 and FAIL_COUNT == 0:
sys.exit(77)
return FAIL_COUNT == 0
if __name__ == "__main__":
sys.exit(0 if main() else 1)
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
#
# multi-msg-record.test
#
# Wrapper around scripts/multi-msg-record.py, invoked by automake via
# `make check`. Verifies that the wolfSSL client correctly processes
# TLS records that carry multiple handshake messages in a single record
# (TLS 1.2 plaintext + renegotiation, and TLS 1.3 encrypted flight).
#
# The test is SKIPped (exit 77) when python3 or tlslite-ng is not
# installed.
#
# if we can, isolate the network namespace to eliminate port collisions.
if [[ -n "$NETWORK_UNSHARE_HELPER" ]]; then
if [[ -z "$NETWORK_UNSHARE_HELPER_CALLED" ]]; then
export NETWORK_UNSHARE_HELPER_CALLED=yes
exec "$NETWORK_UNSHARE_HELPER" "$0" "$@" || exit $?
fi
elif [ "${AM_BWRAPPED-}" != "yes" ]; then
bwrap_path="$(command -v bwrap)"
if [ -n "$bwrap_path" ]; then
export AM_BWRAPPED=yes
exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@"
fi
unset AM_BWRAPPED
fi
# Locate python3 - skip (exit 77) if unavailable.
PYTHON="$(command -v python3)"
if [ -z "$PYTHON" ]; then
echo "python3 not found, skipping multi-msg-record test"
exit 77
fi
SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)"
exec "$PYTHON" "$SCRIPT_DIR/multi-msg-record.py" "$@"
+10 -10
View File
@@ -219,9 +219,9 @@ port4=$(get_first_free_port $((port3 + 1))) # OCSP responder: root-ca
port5=$(get_first_free_port $((port4 + 1))) # TLS server
# Responder 1: intermediate1-ca (server1=valid, server2=revoked)
log1=$(mktemp /tmp/ocsp_resp1.XXXXXX)
log1=$(mktemp "${TMPDIR:-/tmp}/ocsp_resp1.XXXXXX")
resp_logs="$resp_logs $log1"
ready1=$(mktemp /tmp/ocsp_ready1.XXXXXX)
ready1=$(mktemp "${TMPDIR:-/tmp}/ocsp_ready1.XXXXXX")
ready_files="$ready_files $ready1"
$OCSP_RESPONDER -p $port1 -v -R "$ready1" \
-c $OCSP_DIR/intermediate1-ca-cert.pem \
@@ -232,9 +232,9 @@ pid1=$!
resp_pids="$resp_pids $pid1"
# Responder 2: intermediate2-ca (server3=valid, server4=revoked)
log2=$(mktemp /tmp/ocsp_resp2.XXXXXX)
log2=$(mktemp "${TMPDIR:-/tmp}/ocsp_resp2.XXXXXX")
resp_logs="$resp_logs $log2"
ready2=$(mktemp /tmp/ocsp_ready2.XXXXXX)
ready2=$(mktemp "${TMPDIR:-/tmp}/ocsp_ready2.XXXXXX")
ready_files="$ready_files $ready2"
$OCSP_RESPONDER -p $port2 -v -R "$ready2" \
-c $OCSP_DIR/intermediate2-ca-cert.pem \
@@ -245,9 +245,9 @@ pid2=$!
resp_pids="$resp_pids $pid2"
# Responder 3: intermediate3-ca (server5=valid)
log3=$(mktemp /tmp/ocsp_resp3.XXXXXX)
log3=$(mktemp "${TMPDIR:-/tmp}/ocsp_resp3.XXXXXX")
resp_logs="$resp_logs $log3"
ready3=$(mktemp /tmp/ocsp_ready3.XXXXXX)
ready3=$(mktemp "${TMPDIR:-/tmp}/ocsp_ready3.XXXXXX")
ready_files="$ready_files $ready3"
$OCSP_RESPONDER -p $port3 -v -R "$ready3" \
-c $OCSP_DIR/intermediate3-ca-cert.pem \
@@ -258,9 +258,9 @@ pid3=$!
resp_pids="$resp_pids $pid3"
# Responder 4: root-ca (intermediate CAs: 1=valid, 2=valid, 3=revoked)
log4=$(mktemp /tmp/ocsp_resp4.XXXXXX)
log4=$(mktemp "${TMPDIR:-/tmp}/ocsp_resp4.XXXXXX")
resp_logs="$resp_logs $log4"
ready4=$(mktemp /tmp/ocsp_ready4.XXXXXX)
ready4=$(mktemp "${TMPDIR:-/tmp}/ocsp_ready4.XXXXXX")
ready_files="$ready_files $ready4"
$OCSP_RESPONDER -p $port4 -v -R "$ready4" \
-c $OCSP_DIR/root-ca-cert.pem \
@@ -271,9 +271,9 @@ pid4=$!
resp_pids="$resp_pids $pid4"
# Responder 5: authorized responder (delegated OCSP signer with id-kp-OCSPSigning)
log5=$(mktemp /tmp/ocsp_resp5.XXXXXX)
log5=$(mktemp "${TMPDIR:-/tmp}/ocsp_resp5.XXXXXX")
resp_logs="$resp_logs $log5"
ready5=$(mktemp /tmp/ocsp_ready5.XXXXXX)
ready5=$(mktemp "${TMPDIR:-/tmp}/ocsp_ready5.XXXXXX")
ready_files="$ready_files $ready5"
$OCSP_RESPONDER -p $port5 -v -R "$ready5" \
-c $OCSP_DIR/root-ca-cert.pem \
+3 -1
View File
@@ -341,7 +341,9 @@ server=login.live.com
#ca=certs/external/DigiCertGlobalRootCA.pem
ca=./certs/external/ca_collection.pem
if [[ "$V4V6" == "4" ]]; then
if [[ -z "${WOLFSSL_EXTERNAL_TEST-}" || "$WOLFSSL_EXTERNAL_TEST" == "0" ]]; then
echo "Skipping OCSP test on $server (set WOLFSSL_EXTERNAL_TEST=1 to run)"
elif [[ "$V4V6" == "4" ]]; then
retry_with_backoff 3 ./examples/client/client -C -h "$server" -p 443 -A "$ca" -g -W 1
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\n\nClient connection failed" && exit 1
+3 -3
View File
@@ -1904,7 +1904,7 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
#endif /* WOLFSSL_DEBUG_TLS */
/* ignore the message */
*processedSize = idx + fragLength + ssl->keys.padSz;
*processedSize = idx + fragLength;
return 0;
}
@@ -1938,7 +1938,7 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
WOLFSSL_MSG("DTLS1.3 not accepting fragmented plaintext message");
#endif /* WOLFSSL_DEBUG_TLS */
/* ignore the message */
*processedSize = idx + fragLength + ssl->keys.padSz;
*processedSize = idx + fragLength;
return 0;
}
}
@@ -1966,7 +1966,7 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
return DTLS_TOO_MANY_FRAGMENTS_E;
}
*processedSize = idx + fragLength + ssl->keys.padSz;
*processedSize = idx + fragLength;
if (Dtls13NextMessageComplete(ssl))
return Dtls13ProcessBufferedMessages(ssl);
+82 -159
View File
@@ -11905,10 +11905,7 @@ int MsgCheckEncryption(WOLFSSL* ssl, byte type, byte encrypted)
static WC_INLINE int isLastMsg(const WOLFSSL* ssl, word32 msgSz)
{
word32 extra = 0;
if (IsEncryptionOn(ssl, 0))
extra = ssl->keys.padSz;
return (ssl->buffers.inputBuffer.idx - ssl->curStartIdx) + msgSz + extra
return (ssl->buffers.inputBuffer.idx - ssl->curStartIdx) + msgSz
== ssl->curSize;
}
@@ -17698,9 +17695,6 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
ssl->options.serverState = SERVER_CERT_COMPLETE;
}
if (IsEncryptionOn(ssl, 0))
args->idx += ssl->keys.padSz;
/* Advance state and proceed */
ssl->options.asyncState = TLS_ASYNC_END;
} /* case TLS_ASYNC_FINALIZE */
@@ -17964,12 +17958,6 @@ static int DoCertificateStatus(WOLFSSL* ssl, byte* input, word32* inOutIdx,
SendAlert(ssl, alert_fatal, bad_certificate_status_response);
}
if (IsEncryptionOn(ssl, 0)) {
if (*inOutIdx + ssl->keys.padSz > size)
return BUFFER_E;
*inOutIdx += ssl->keys.padSz;
}
WOLFSSL_LEAVE("DoCertificateStatus", ret);
WOLFSSL_END(WC_FUNC_CERTIFICATE_STATUS_DO);
@@ -17984,10 +17972,9 @@ static int DoCertificateStatus(WOLFSSL* ssl, byte* input, word32* inOutIdx,
#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
static int DoHelloRequest(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
word32 size, word32 totalSz)
static int DoHelloRequest(WOLFSSL* ssl, word32 size)
{
(void)input;
(void)size;
WOLFSSL_START(WC_FUNC_HELLO_REQUEST_DO);
WOLFSSL_ENTER("DoHelloRequest");
@@ -17995,17 +17982,6 @@ static int DoHelloRequest(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
if (size) /* must be 0 */
return BUFFER_ERROR;
if (IsEncryptionOn(ssl, 0)) {
/* If size == totalSz then we are in DtlsMsgDrain so no need to worry
* about padding */
/* access beyond input + size should be checked against totalSz */
if (size != totalSz &&
*inOutIdx + ssl->keys.padSz > totalSz)
return BUFFER_E;
*inOutIdx += ssl->keys.padSz;
}
if (ssl->options.side == WOLFSSL_SERVER_END) {
SendAlert(ssl, alert_fatal, unexpected_message); /* try */
WOLFSSL_ERROR_VERBOSE(FATAL_ERROR);
@@ -18040,7 +18016,7 @@ int DoFinished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size,
* If size == totalSz then we are in DtlsMsgDrain so no need to worry about
* padding */
if (size != totalSz) {
if (*inOutIdx + size + ssl->keys.padSz > totalSz)
if (*inOutIdx + size > totalSz)
return BUFFER_E;
}
@@ -18083,8 +18059,7 @@ int DoFinished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size,
}
#endif
/* force input exhaustion at ProcessReply consuming padSz */
*inOutIdx += size + ssl->keys.padSz;
*inOutIdx += size;
if (ssl->options.side == WOLFSSL_CLIENT_END) {
ssl->options.serverState = SERVER_FINISHED_COMPLETE;
@@ -18737,8 +18712,7 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return INCOMPLETE_DATA;
}
expectedIdx = *inOutIdx + size +
(ssl->keys.encryptionOn ? ssl->keys.padSz : 0);
expectedIdx = *inOutIdx + size;
#if !defined(NO_WOLFSSL_SERVER) && \
defined(HAVE_SECURE_RENEGOTIATION) && \
@@ -18903,21 +18877,13 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
case hello_request:
WOLFSSL_MSG("processing hello request");
ret = DoHelloRequest(ssl, input, inOutIdx, size, totalSz);
ret = DoHelloRequest(ssl, size);
break;
#ifndef NO_WOLFSSL_CLIENT
case hello_verify_request:
WOLFSSL_MSG("processing hello verify request");
ret = DoHelloVerifyRequest(ssl, input,inOutIdx, size);
if (IsEncryptionOn(ssl, 0)) {
/* access beyond input + size should be checked against totalSz
*/
if (*inOutIdx + ssl->keys.padSz > totalSz)
return BUFFER_E;
*inOutIdx += ssl->keys.padSz;
}
break;
case server_hello:
@@ -18989,8 +18955,6 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
AddLateName("ServerHelloDone", &ssl->timeoutInfo);
#endif
ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
if (IsEncryptionOn(ssl, 0))
*inOutIdx += ssl->keys.padSz;
break;
case finished:
@@ -19022,16 +18986,6 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
}
#endif
/* If size == totalSz then we are in DtlsMsgDrain so no need to worry
* about padding */
if (IsEncryptionOn(ssl, 0)) {
/* access beyond input + size should be checked against totalSz
*/
if (size != totalSz &&
*inOutIdx + ssl->keys.padSz > totalSz)
return BUFFER_E;
*inOutIdx += ssl->keys.padSz;
}
break;
case client_key_exchange:
@@ -19132,7 +19086,11 @@ static int DoHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
return DoHandShakeMsgType(ssl, input, inOutIdx, type, size, totalSz);
}
inputLength = ssl->buffers.inputBuffer.length - *inOutIdx;
/* totalSz is now curStartIdx + curSize (content-only, padSz already
* subtracted in ProcessReply). */
if (*inOutIdx > totalSz)
return BUFFER_ERROR;
inputLength = totalSz - *inOutIdx;
/* If there is a pending fragmented handshake message,
* pending message size will be non-zero. */
@@ -19893,11 +19851,6 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
input + *inOutIdx, size, type,
fragOffset, fragSz, ssl->heap);
*inOutIdx += fragSz;
if (*inOutIdx + ssl->keys.padSz > totalSz) {
WOLFSSL_ERROR(BUFFER_E);
return BUFFER_E;
}
*inOutIdx += ssl->keys.padSz;
ret = 0;
#ifndef WOLFSSL_DTLS_RESEND_ONLY_TIMEOUT
/* If we receive an out of order last flight msg then retransmit */
@@ -19936,10 +19889,6 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
/* Already saw this message and processed it. It can be ignored. */
WOLFSSL_MSG("Already saw this message and processed it");
*inOutIdx += fragSz;
if (*inOutIdx + ssl->keys.padSz > totalSz) {
WOLFSSL_ERROR(BUFFER_E);
return BUFFER_E;
}
#ifndef WOLFSSL_DTLS_RESEND_ONLY_TIMEOUT
if (IsDtlsNotSctpMode(ssl) &&
VerifyForDtlsMsgPoolSend(ssl, type, fragOffset)) {
@@ -19947,7 +19896,6 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
ret = DtlsMsgPoolSend(ssl, 0);
}
#endif
*inOutIdx += ssl->keys.padSz;
}
else if (fragSz < size) {
/* Since this branch is in order, but fragmented, dtls_rx_msg_list will
@@ -19971,11 +19919,6 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
input + *inOutIdx, size, type,
fragOffset, fragSz, ssl->heap);
*inOutIdx += fragSz;
if (*inOutIdx + ssl->keys.padSz > totalSz) {
WOLFSSL_ERROR(BUFFER_E);
return BUFFER_E;
}
*inOutIdx += ssl->keys.padSz;
ret = 0;
if (ssl->dtls_rx_msg_list != NULL && ssl->dtls_rx_msg_list->ready)
ret = DtlsMsgDrain(ssl);
@@ -19992,9 +19935,9 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
WOLFSSL_ERROR(BUFFER_ERROR);
return BUFFER_ERROR;
}
if (idx + fragSz + ssl->keys.padSz > totalSz)
if (idx + fragSz > totalSz)
return BUFFER_E;
*inOutIdx = idx + fragSz + ssl->keys.padSz;
*inOutIdx = idx + fragSz;
/* In async mode always store the message and process it with
* DtlsMsgDrain because in case of a WC_PENDING_E it will be
* easier this way. */
@@ -22147,7 +22090,7 @@ int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff)
}
#endif
dataSz = (int)(msgSz - ssl->keys.padSz);
dataSz = (int)msgSz;
if (dataSz < 0) {
WOLFSSL_MSG("App data buffer error, malicious input?");
if (sniff == NO_SNIFF) {
@@ -22202,8 +22145,6 @@ int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff)
ssl->buffers.clearOutputBuffer.length = (unsigned int)dataSz;
}
idx += ssl->keys.padSz;
#ifdef HAVE_LIBZ
/* decompress could be bigger, overwrite after verify */
if (ssl->options.usingCompression)
@@ -22494,9 +22435,6 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
}
#endif
if (IsEncryptionOn(ssl, 0))
dataSz -= ssl->keys.padSz;
/* make sure can read the message */
if (dataSz != ALERT_SIZE) {
#ifdef WOLFSSL_EXTRA_ALERTS
@@ -22565,9 +22503,6 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
WOLFSSL_ERROR(*type);
}
}
if (IsEncryptionOn(ssl, 0))
*inOutIdx += ssl->keys.padSz;
return level;
}
@@ -23575,6 +23510,14 @@ default:
}
/* Reduce curSize to content only, excluding padding/MAC overhead.
* padSz is accounted for once at the end of record processing. */
if (IsEncryptionOn(ssl, 0)) {
if (ssl->keys.padSz > ssl->curSize)
return BUFFER_E;
ssl->curSize -= (word16)ssl->keys.padSz;
}
/* in case > 1 msg per record */
ssl->curStartIdx = ssl->buffers.inputBuffer.idx;
@@ -23592,7 +23535,8 @@ default:
WOLFSSL_MSG("Dropping DTLS record outside receiving "
"window");
ssl->options.processReply = doProcessInit;
ssl->buffers.inputBuffer.idx += ssl->curSize;
ssl->buffers.inputBuffer.idx += ssl->curSize
+ ssl->keys.padSz;
if (ssl->buffers.inputBuffer.idx >
ssl->buffers.inputBuffer.length)
return BUFFER_E;
@@ -23632,7 +23576,7 @@ default:
}
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
if (IsEncryptionOn(ssl, 0) && ssl->options.startedETMRead) {
if ((ssl->curSize - ssl->keys.padSz > MAX_PLAINTEXT_SZ)
if ((ssl->curSize > MAX_PLAINTEXT_SZ)
#ifdef WOLFSSL_ASYNC_CRYPT
&& ssl->buffers.inputBuffer.length !=
ssl->buffers.inputBuffer.idx
@@ -23650,7 +23594,7 @@ default:
#endif
/* TLS13 plaintext limit is checked earlier before decryption */
if (!IsAtLeastTLSv1_3(ssl->version)
&& ssl->curSize - ssl->keys.padSz > MAX_PLAINTEXT_SZ
&& ssl->curSize > MAX_PLAINTEXT_SZ
#ifdef WOLFSSL_ASYNC_CRYPT
&& ssl->buffers.inputBuffer.length !=
ssl->buffers.inputBuffer.idx
@@ -23676,7 +23620,7 @@ default:
ret = DoDtlsHandShakeMsg(ssl,
ssl->buffers.inputBuffer.buffer,
&ssl->buffers.inputBuffer.idx,
ssl->buffers.inputBuffer.length);
ssl->curStartIdx + ssl->curSize);
if (ret == 0 ||
ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
/* Reset timeout as we have received a valid
@@ -23696,7 +23640,7 @@ default:
ret = Dtls13HandshakeRecv(ssl,
ssl->buffers.inputBuffer.buffer,
&ssl->buffers.inputBuffer.idx,
ssl->buffers.inputBuffer.length);
ssl->curStartIdx + ssl->curSize);
#ifdef WOLFSSL_EARLY_DATA
if (ret == 0 &&
ssl->options.side == WOLFSSL_SERVER_END &&
@@ -23707,8 +23651,12 @@ default:
exit */
ssl->earlyData = no_early_data;
ssl->options.processReply = doProcessInit;
if (ssl->options.clientInEarlyData)
if (ssl->options.clientInEarlyData) {
if (IsEncryptionOn(ssl, 0))
ssl->buffers.inputBuffer.idx +=
ssl->keys.padSz;
return APP_DATA_READY;
}
}
#endif /* WOLFSSL_EARLY_DATA */
if (ret == 0 ||
@@ -23729,7 +23677,7 @@ default:
ret = DoHandShakeMsg(ssl,
ssl->buffers.inputBuffer.buffer,
&ssl->buffers.inputBuffer.idx,
ssl->buffers.inputBuffer.length);
ssl->curStartIdx + ssl->curSize);
if (ret != 0) {
if (SendFatalAlertOnly(ssl, ret) ==
WC_NO_ERR_TRACE(SOCKET_ERROR_E))
@@ -23745,7 +23693,7 @@ default:
ret = DoTls13HandShakeMsg(ssl,
ssl->buffers.inputBuffer.buffer,
&ssl->buffers.inputBuffer.idx,
ssl->buffers.inputBuffer.length);
ssl->curStartIdx + ssl->curSize);
#ifdef WOLFSSL_EARLY_DATA
if (ret != 0)
return ret;
@@ -23754,8 +23702,12 @@ default:
ssl->options.handShakeState == HANDSHAKE_DONE) {
ssl->earlyData = no_early_data;
ssl->options.processReply = doProcessInit;
if (ssl->options.clientInEarlyData)
if (ssl->options.clientInEarlyData) {
if (IsEncryptionOn(ssl, 0))
ssl->buffers.inputBuffer.idx +=
ssl->keys.padSz;
return APP_DATA_READY;
}
}
#endif
#else
@@ -23855,11 +23807,6 @@ default:
return LENGTH_ERROR;
}
if (IsEncryptionOn(ssl, 0) && ssl->options.handShakeDone) {
ssl->buffers.inputBuffer.idx += ssl->keys.padSz;
ssl->curSize -= (word16)ssl->keys.padSz;
}
if (ssl->curSize != 1) {
WOLFSSL_MSG("Malicious or corrupted ChangeCipher msg");
WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR);
@@ -24023,11 +23970,10 @@ default:
word32 processedSize = 0;
ret = DoDtls13Ack(ssl, ssl->buffers.inputBuffer.buffer +
ssl->buffers.inputBuffer.idx,
ssl->buffers.inputBuffer.length -
ssl->buffers.inputBuffer.idx -
ssl->keys.padSz, &processedSize);
ssl->curStartIdx + ssl->curSize -
ssl->buffers.inputBuffer.idx,
&processedSize);
ssl->buffers.inputBuffer.idx += processedSize;
ssl->buffers.inputBuffer.idx += ssl->keys.padSz;
if (ret != 0)
return ret;
break;
@@ -24041,53 +23987,44 @@ default:
ssl->options.processReply = doProcessInit;
/* input exhausted */
if (ssl->buffers.inputBuffer.idx >= ssl->buffers.inputBuffer.length
#ifdef WOLFSSL_DTLS
|| (ssl->options.dtls &&
/* If app data was processed then return now to avoid
* dropping any app data. */
(ssl->curRL.type == application_data ||
/* client: if we processed a finished message, return to
* allow higher layers to establish the crypto
* parameters of the connection. The remaining data
* may be app data that we would drop without the
* crypto setup. */
(ssl->options.side == WOLFSSL_CLIENT_END &&
ssl->options.serverState == SERVER_FINISHED_COMPLETE &&
ssl->options.handShakeState != HANDSHAKE_DONE)))
#endif
) {
/* Shrink input buffer when we successfully finish record
* processing */
if ((ret == 0) && ssl->buffers.inputBuffer.dynamicFlag)
ShrinkInputBuffer(ssl, NO_FORCED_FREE);
return ret;
}
/* more messages per record */
else if ((ssl->buffers.inputBuffer.idx - ssl->curStartIdx)
if ((ssl->buffers.inputBuffer.idx - ssl->curStartIdx)
< ssl->curSize) {
WOLFSSL_MSG("More messages in record");
ssl->options.processReply = runProcessingOneMessage;
if (IsEncryptionOn(ssl, 0)) {
/* With encryption on, we advance the index by the value
* of ssl->keys.padSz. Since padding only appears once, we
* only can do this at the end of record parsing. We have to
* reset the index to the start of the next message here. */
if (ssl->buffers.inputBuffer.idx >= ssl->keys.padSz) {
ssl->buffers.inputBuffer.idx -= ssl->keys.padSz;
}
else {
WOLFSSL_MSG("\tBuffer advanced not enough error");
WOLFSSL_ERROR_VERBOSE(FATAL_ERROR);
return FATAL_ERROR;
}
}
}
/* more records */
else {
/* Done with this record. Advance past padding/MAC. */
if (IsEncryptionOn(ssl, 0))
ssl->buffers.inputBuffer.idx += ssl->keys.padSz;
/* input exhausted */
if (ssl->buffers.inputBuffer.idx >=
ssl->buffers.inputBuffer.length
#ifdef WOLFSSL_DTLS
|| (ssl->options.dtls &&
/* If app data was processed then return now to avoid
* dropping any app data. */
(ssl->curRL.type == application_data ||
/* client: if we processed a finished message, return
* to allow higher layers to establish the
* crypto parameters of the connection. The
* remaining data may be app data that we would
* drop without the crypto setup. */
(ssl->options.side == WOLFSSL_CLIENT_END &&
ssl->options.serverState ==
SERVER_FINISHED_COMPLETE &&
ssl->options.handShakeState != HANDSHAKE_DONE)))
#endif
) {
/* Shrink input buffer when we successfully finish record
* processing */
if ((ret == 0) && ssl->buffers.inputBuffer.dynamicFlag)
ShrinkInputBuffer(ssl, NO_FORCED_FREE);
return ret;
}
/* more records */
WOLFSSL_MSG("More records in input");
}
#ifdef WOLFSSL_ASYNC_CRYPT
@@ -24102,10 +24039,14 @@ default:
if (ret == WC_NO_ERR_TRACE(APP_DATA_READY))
return ret;
#endif
/* It is safe to shrink the input buffer here now. local vars will
* be reset to the new starting value. */
if (ret == 0 && ssl->buffers.inputBuffer.dynamicFlag)
/* It is safe to shrink the input buffer here now, but only
* when done with the current record. During multi-message
* record processing, shrinking would invalidate curStartIdx
* and curSize. */
if (ssl->options.processReply != runProcessingOneMessage
&& ret == 0 && ssl->buffers.inputBuffer.dynamicFlag) {
ShrinkInputBuffer(ssl, NO_FORCED_FREE);
}
continue;
default:
WOLFSSL_MSG("Bad process input state, programming error");
@@ -32166,9 +32107,6 @@ static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
ssl->options.serverState = SERVER_HELLO_COMPLETE;
if (IsEncryptionOn(ssl, 0))
*inOutIdx += ssl->keys.padSz;
#ifdef HAVE_SECRET_CALLBACK
if (ssl->sessionSecretCb != NULL
#ifdef HAVE_SESSION_TICKET
@@ -32500,9 +32438,6 @@ static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
ssl->options.sendVerify = SEND_BLANK_CERT;
}
if (IsEncryptionOn(ssl, 0))
*inOutIdx += ssl->keys.padSz;
WOLFSSL_LEAVE("DoCertificateRequest", 0);
WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_DO);
@@ -33742,9 +33677,6 @@ static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input,
case TLS_ASYNC_FINALIZE:
{
if (IsEncryptionOn(ssl, 0))
args->idx += ssl->keys.padSz;
/* Advance state and proceed */
ssl->options.asyncState = TLS_ASYNC_END;
} /* case TLS_ASYNC_FINALIZE */
@@ -35512,9 +35444,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#endif
}
if (IsEncryptionOn(ssl, 0))
*inOutIdx += ssl->keys.padSz;
ssl->expect_session_ticket = 0;
return 0;
@@ -39343,9 +39272,6 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
case TLS_ASYNC_FINALIZE:
{
if (IsEncryptionOn(ssl, 0))
args->idx += ssl->keys.padSz;
ssl->options.havePeerVerify = 1;
/* Set final index */
@@ -42428,9 +42354,6 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ],
case TLS_ASYNC_FINALIZE:
{
if (IsEncryptionOn(ssl, 0))
args->idx += ssl->keys.padSz;
ret = MakeMasterSecret(ssl);
/* Check for error */
+10 -24
View File
@@ -6019,9 +6019,6 @@ static int DoTls13EncryptedExtensions(WOLFSSL* ssl, const byte* input,
/* Move index to byte after message. */
*inOutIdx = i + totalExtSz;
/* Always encrypted. */
*inOutIdx += ssl->keys.padSz;
#ifdef WOLFSSL_EARLY_DATA
if (ssl->earlyData != no_early_data) {
TLSX* ext = TLSX_Find(ssl->extensions, TLSX_EARLY_DATA);
@@ -6165,9 +6162,6 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input,
#endif
}
/* This message is always encrypted so add encryption padding. */
*inOutIdx += ssl->keys.padSz;
#ifdef WOLFSSL_POST_HANDSHAKE_AUTH
{
/* CertReqCtx has one byte at end for context value.
@@ -11636,9 +11630,6 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
args->idx += args->sz;
*inOutIdx = args->idx;
/* Encryption is always on: add padding */
*inOutIdx += ssl->keys.padSz;
/* Advance state and proceed */
ssl->options.asyncState = TLS_ASYNC_END;
@@ -11836,8 +11827,7 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
}
/* Force input exhaustion at ProcessReply by consuming padSz. */
*inOutIdx += size + ssl->keys.padSz;
*inOutIdx += size;
#ifndef NO_WOLFSSL_SERVER
if (ssl->options.side == WOLFSSL_SERVER_END &&
@@ -12290,8 +12280,6 @@ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
/* Move index to byte after message. */
*inOutIdx += totalSz;
/* Always encrypted. */
*inOutIdx += ssl->keys.padSz;
/* Future traffic uses new decryption keys. */
if ((ret = DeriveTls13Keys(ssl, update_traffic_key, DECRYPT_SIDE_ONLY, 1))
@@ -12442,9 +12430,6 @@ static int DoTls13EndOfEarlyData(WOLFSSL* ssl, const byte* input,
ssl->earlyData = done_early_data;
/* Always encrypted. */
*inOutIdx += ssl->keys.padSz;
ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY);
WOLFSSL_LEAVE("DoTls13EndOfEarlyData", ret);
@@ -12616,9 +12601,6 @@ static int DoTls13NewSessionTicket(WOLFSSL* ssl, const byte* input,
AddSession(ssl);
#endif
/* Always encrypted. */
*inOutIdx += ssl->keys.padSz;
ssl->expect_session_ticket = 0;
#else
(void)ssl;
@@ -12626,7 +12608,7 @@ static int DoTls13NewSessionTicket(WOLFSSL* ssl, const byte* input,
WOLFSSL_ENTER("DoTls13NewSessionTicket");
*inOutIdx += size + ssl->keys.padSz;
*inOutIdx += size;
#endif /* HAVE_SESSION_TICKET */
WOLFSSL_LEAVE("DoTls13NewSessionTicket", 0);
@@ -13969,7 +13951,11 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
totalSz);
}
inputLength = ssl->buffers.inputBuffer.length - *inOutIdx - ssl->keys.padSz;
/* totalSz is now curStartIdx + curSize (content-only, padSz already
* subtracted in ProcessReply). */
if (*inOutIdx > totalSz)
return BUFFER_ERROR;
inputLength = totalSz - *inOutIdx;
/* If there is a pending fragmented handshake message,
* pending message size will be non-zero. */
@@ -14011,7 +13997,7 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
input + *inOutIdx - HANDSHAKE_HEADER_SZ,
inputLength);
ssl->arrays->pendingMsgOffset = inputLength;
*inOutIdx += inputLength + ssl->keys.padSz - HANDSHAKE_HEADER_SZ;
*inOutIdx += inputLength - HANDSHAKE_HEADER_SZ;
return 0;
}
@@ -14035,7 +14021,7 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
XMEMCPY(ssl->arrays->pendingMsg + ssl->arrays->pendingMsgOffset,
input + *inOutIdx, inputLength);
ssl->arrays->pendingMsgOffset += inputLength;
*inOutIdx += inputLength + ssl->keys.padSz;
*inOutIdx += inputLength;
if (ssl->arrays->pendingMsgOffset == ssl->arrays->pendingMsgSz)
{
@@ -14050,7 +14036,7 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
/* setup to process fragment again */
ssl->arrays->pendingMsgOffset -= inputLength;
*inOutIdx -= inputLength + ssl->keys.padSz;
*inOutIdx -= inputLength;
}
else
#endif