Merge pull request #7338 from yanrayw/2.28-6500-gitignore-fix

Backport 2.28: fix: ignore *.o under tests/src/test_helpers
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..cef07bf
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,26 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the version of Python and other tools you might need
+build:
+  os: ubuntu-20.04
+  tools:
+    python: "3.9"
+  jobs:
+    pre_build:
+      - make apidoc
+      - breathe-apidoc -o docs/api apidoc/xml
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+  builder: dirhtml
+  configuration: docs/conf.py
+
+# Optionally declare the Python requirements required to build your docs
+python:
+  install:
+  - requirements: docs/requirements.txt
diff --git a/.travis.yml b/.travis.yml
index eb01a44..ada8fc5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -70,6 +70,7 @@
       os: windows
       script:
         - scripts/windows_msbuild.bat v141 # Visual Studio 2017
+        - visualc/VS2010/x64/Release/selftest.exe --ci
 
 after_failure:
 - tests/scripts/travis-log-failure.sh
diff --git a/BRANCHES.md b/BRANCHES.md
index d4fa506..680c0ca 100644
--- a/BRANCHES.md
+++ b/BRANCHES.md
@@ -71,6 +71,6 @@
 - [`development`](https://github.com/Mbed-TLS/mbedtls/)
 - [`mbedtls-2.28`](https://github.com/Mbed-TLS/mbedtls/tree/mbedtls-2.28)
  maintained until at least the end of 2024, see
-  <https://github.com/Mbed-TLS/mbedtls/releases/tag/v2.28.2>.
+  <https://github.com/Mbed-TLS/mbedtls/releases/tag/v2.28.3>.
 
 Users are urged to always use the latest version of a maintained branch.
diff --git a/ChangeLog b/ChangeLog
index 23be7dd..4033839 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,84 @@
 Mbed TLS ChangeLog (Sorted per branch, date)
 
+= Mbed TLS 2.28.3 branch released 2023-03-28
+
+Features
+   * Use HOSTCC (if it is set) when compiling C code during generation of the
+     configuration-independent files. This allows them to be generated when
+     CC is set for cross compilation.
+   * AES-NI is now supported with Visual Studio.
+   * AES-NI is now supported in 32-bit builds, or when MBEDTLS_HAVE_ASM
+     is disabled, when compiling with GCC or Clang or a compatible compiler
+     for a target CPU that supports the requisite instructions (for example
+     gcc -m32 -msse2 -maes -mpclmul). (Generic x86 builds with GCC-like
+     compilers still require MBEDTLS_HAVE_ASM and a 64-bit target.)
+
+Security
+   * MBEDTLS_AESNI_C, which is enabled by default, was silently ignored on
+     builds that couldn't compile the GCC-style assembly implementation
+     (most notably builds with Visual Studio), leaving them vulnerable to
+     timing side-channel attacks. There is now an intrinsics-based AES-NI
+     implementation as a fallback for when the assembly one cannot be used.
+
+Bugfix
+   * Fix a build issue on Windows where the source and build directory could
+     not be on different drives (#5751).
+   * Fix possible integer overflow in mbedtls_timing_hardclock(), which
+     could cause a crash for certain platforms & compiler options.
+   * Fix IAR compiler warnings. Fixes #6924.
+   * Fix a bug in the build where directory names containing spaces were
+     causing generate_errors.pl to error out resulting in a build failure.
+     Fixes issue #6879.
+   * Fix compile error where MBEDTLS_RSA_C and MBEDTLS_X509_CRT_WRITE_C are
+     defined, but MBEDTLS_PK_RSA_ALT_SUPPORT is not defined. Fixes #3174.
+   * Fix a build issue when defining MBEDTLS_TIMING_ALT and MBEDTLS_SELF_TEST.
+     The library would not link if the user didn't provide an external self-test
+     function. The self-test is now provided regardless of the choice of
+     internal/alternative timing implementation. Fixes #6923.
+   * mbedtls_x509write_crt_set_serial() now explicitly rejects serial numbers
+     whose binary representation is longer than 20 bytes. This was already
+     forbidden by the standard (RFC5280 - section 4.1.2.2) and now it's being
+     enforced also at code level.
+   * Fix potential undefined behavior in mbedtls_mpi_sub_abs().  Reported by
+     Pascal Cuoq using TrustInSoft Analyzer in #6701; observed independently by
+     Aaron Ucko under Valgrind.
+   * Fix behavior of certain sample programs which could, when run with no
+     arguments, access uninitialized memory in some cases. Fixes #6700 (which
+     was found by TrustInSoft Analyzer during REDOCS'22) and #1120.
+   * Fix build errors in test programs when MBEDTLS_CERTS_C is disabled.
+     Fixes #6243.
+   * Fix parsing of X.509 SubjectAlternativeName extension. Previously,
+     malformed alternative name components were not caught during initial
+     certificate parsing, but only on subsequent calls to
+     mbedtls_x509_parse_subject_alt_name(). Fixes #2838.
+   * Fix bug in conversion from OID to string in
+     mbedtls_oid_get_numeric_string(). OIDs such as 2.40.0.25 are now printed
+     correctly.
+   * Reject OIDs with overlong-encoded subidentifiers when converting
+     them to a string.
+   * Reject OIDs with subidentifier values exceeding UINT_MAX.  Such
+     subidentifiers can be valid, but Mbed TLS cannot currently handle them.
+   * Reject OIDs that have unterminated subidentifiers, or (equivalently)
+     have the most-significant bit set in their last byte.
+   * Silence a warning about an unused local variable in bignum.c on
+     some architectures. Fixes #7166.
+   * Silence warnings from clang -Wdocumentation about empty \retval
+     descriptions, which started appearing with Clang 15. Fixes #6960.
+   * Fix undefined behavior in mbedtls_ssl_read() and mbedtls_ssl_write() if
+     len argument is 0 and buffer is NULL.
+
+Changes
+   * The C code follows a new coding style. This is transparent for users but
+     affects contributors and maintainers of local patches. For more
+     information, see
+     https://mbed-tls.readthedocs.io/en/latest/kb/how-to/rewrite-branch-for-coding-style/
+   * Changed the default MBEDTLS_ECP_WINDOW_SIZE from 6 to 2.
+     As tested in issue 6790, the correlation between this define and
+     RSA decryption performance has changed lately due to security fixes.
+     To fix the performance degradation when using default values the
+     window was reduced from 6 to 2, a value that gives the best or close
+     to best results when tested on Cortex-M4 and Intel i7.
+
 = Mbed TLS 2.28.2 branch released 2022-12-14
 
 Security
diff --git a/ChangeLog.d/build-without-certs.txt b/ChangeLog.d/build-without-certs.txt
deleted file mode 100644
index dcb5c77..0000000
--- a/ChangeLog.d/build-without-certs.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugfix
-   * Fix build errors in test programs when MBEDTLS_CERTS_C is disabled.
-     Fixes #6243.
diff --git a/ChangeLog.d/c-build-helper-hostcc.txt b/ChangeLog.d/c-build-helper-hostcc.txt
deleted file mode 100644
index 86182c3..0000000
--- a/ChangeLog.d/c-build-helper-hostcc.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Features
-   * Use HOSTCC (if it is set) when compiling C code during generation of the
-     configuration-independent files. This allows them to be generated when
-     CC is set for cross compilation.
diff --git a/ChangeLog.d/clang-15-bignum-warning.txt b/ChangeLog.d/clang-15-bignum-warning.txt
deleted file mode 100644
index d3308b4..0000000
--- a/ChangeLog.d/clang-15-bignum-warning.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugfix
-   * Silence a warning about an unused local variable in bignum.c on
-     some architectures. Fixes #7166.
diff --git a/ChangeLog.d/coding-style.txt b/ChangeLog.d/coding-style.txt
deleted file mode 100644
index b2cff5c..0000000
--- a/ChangeLog.d/coding-style.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Changes
-   * The C code follows a new coding style. This is transparent for users but
-     affects contributors and maintainers of local patches. For more
-     information, see
-     https://mbed-tls.readthedocs.io/en/latest/kb/how-to/rewrite-branch-for-coding-style/
diff --git a/ChangeLog.d/conditionalize-mbedtls_mpi_sub_abs-memcpy.txt b/ChangeLog.d/conditionalize-mbedtls_mpi_sub_abs-memcpy.txt
deleted file mode 100644
index 0a90721..0000000
--- a/ChangeLog.d/conditionalize-mbedtls_mpi_sub_abs-memcpy.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Bugfix
-   * Fix potential undefined behavior in mbedtls_mpi_sub_abs().  Reported by
-     Pascal Cuoq using TrustInSoft Analyzer in #6701; observed independently by
-     Aaron Ucko under Valgrind.
diff --git a/ChangeLog.d/empty-retval-description.txt b/ChangeLog.d/empty-retval-description.txt
deleted file mode 100644
index 491adf5..0000000
--- a/ChangeLog.d/empty-retval-description.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugfix
-   * Silence warnings from clang -Wdocumentation about empty \retval
-     descriptions, which started appearing with Clang 15. Fixes #6960.
diff --git a/ChangeLog.d/fix-example-programs-no-args.txt b/ChangeLog.d/fix-example-programs-no-args.txt
deleted file mode 100644
index 57fe37a..0000000
--- a/ChangeLog.d/fix-example-programs-no-args.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Bugfix
-   * Fix behavior of certain sample programs which could, when run with no
-     arguments, access uninitialized memory in some cases. Fixes #6700 (which
-     was found by TrustInSoft Analyzer during REDOCS'22) and #1120.
diff --git a/ChangeLog.d/fix-gettimeofday-overflow.txt b/ChangeLog.d/fix-gettimeofday-overflow.txt
deleted file mode 100644
index 99a049d..0000000
--- a/ChangeLog.d/fix-gettimeofday-overflow.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Bugfix
-   * Fix possible integer overflow in mbedtls_timing_hardclock(), which
-     could cause a crash for certain platforms & compiler options.
-
diff --git a/ChangeLog.d/fix-iar-warnings.txt b/ChangeLog.d/fix-iar-warnings.txt
deleted file mode 100644
index 8a30132..0000000
--- a/ChangeLog.d/fix-iar-warnings.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Bugfix
-   * Fix IAR compiler warnings. Fixes #6924.
diff --git a/ChangeLog.d/fix-oid-to-string-bugs.txt b/ChangeLog.d/fix-oid-to-string-bugs.txt
deleted file mode 100644
index 3cf02c3..0000000
--- a/ChangeLog.d/fix-oid-to-string-bugs.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Bugfix
-   * Fix bug in conversion from OID to string in
-     mbedtls_oid_get_numeric_string(). OIDs such as 2.40.0.25 are now printed
-     correctly.
-   * Reject OIDs with overlong-encoded subidentifiers when converting
-     them to a string.
-   * Reject OIDs with subidentifier values exceeding UINT_MAX.  Such
-     subidentifiers can be valid, but Mbed TLS cannot currently handle them.
-   * Reject OIDs that have unterminated subidentifiers, or (equivalently)
-     have the most-significant bit set in their last byte.
diff --git a/ChangeLog.d/fix-rsaalt-test-guards.txt b/ChangeLog.d/fix-rsaalt-test-guards.txt
deleted file mode 100644
index f4f39c9..0000000
--- a/ChangeLog.d/fix-rsaalt-test-guards.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugfix
-   * Fix compile error where MBEDTLS_RSA_C and MBEDTLS_X509_CRT_WRITE_C are
-     defined, but MBEDTLS_PK_RSA_ALT_SUPPORT is not defined. Fixes #3174.
diff --git a/ChangeLog.d/fix_build_for_directory_names_containing_spaces.txt b/ChangeLog.d/fix_build_for_directory_names_containing_spaces.txt
deleted file mode 100644
index e7643b7..0000000
--- a/ChangeLog.d/fix_build_for_directory_names_containing_spaces.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Bugfix
-   * Fix a bug in the build where directory names containing spaces were
-     causing generate_errors.pl to error out resulting in a build failure.
-     Fixes issue #6879.
diff --git a/ChangeLog.d/fix_hard_link_across_drives b/ChangeLog.d/fix_hard_link_across_drives
deleted file mode 100644
index 0c55c30..0000000
--- a/ChangeLog.d/fix_hard_link_across_drives
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugfix
-   * Fix a build issue on Windows where the source and build directory could not be on
-     different drives (#5751).
diff --git a/ChangeLog.d/fix_timing_alt.txt b/ChangeLog.d/fix_timing_alt.txt
deleted file mode 100644
index 86ec16d..0000000
--- a/ChangeLog.d/fix_timing_alt.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Bugfix
-   * Fix a build issue when defining MBEDTLS_TIMING_ALT and MBEDTLS_SELF_TEST.
-     The library would not link if the user didn't provide an external self-test
-     function. The self-test is now provided regardless of the choice of
-     internal/alternative timing implementation. Fixes #6923.
diff --git a/ChangeLog.d/improve_x509_cert_writing_serial_number_management.txt b/ChangeLog.d/improve_x509_cert_writing_serial_number_management.txt
deleted file mode 100644
index a85c79b..0000000
--- a/ChangeLog.d/improve_x509_cert_writing_serial_number_management.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Bugfix
-   * mbedtls_x509write_crt_set_serial() now explicitly rejects serial numbers
-     whose binary representation is longer than 20 bytes. This was already
-     forbidden by the standard (RFC5280 - section 4.1.2.2) and now it's being
-     enforced also at code level.
diff --git a/ChangeLog.d/mbedtls_ssl_read_undefined_behavior.txt b/ChangeLog.d/mbedtls_ssl_read_undefined_behavior.txt
deleted file mode 100644
index 1f2c563..0000000
--- a/ChangeLog.d/mbedtls_ssl_read_undefined_behavior.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Bugfix
-   * Fix undefined behavior in mbedtls_ssl_read() and mbedtls_ssl_write() if
-     len argument is 0 and buffer is NULL.
diff --git a/ChangeLog.d/mpi-window-perf b/ChangeLog.d/mpi-window-perf
deleted file mode 100644
index 0f75d6a..0000000
--- a/ChangeLog.d/mpi-window-perf
+++ /dev/null
@@ -1,7 +0,0 @@
-Changes
-   * Changed the default MBEDTLS_ECP_WINDOW_SIZE from 6 to 2.
-     As tested in issue 6790, the correlation between this define and
-     RSA decryption performance has changed lately due to security fixes.
-     To fix the performance degradation when using default values the
-     window was reduced from 6 to 2, a value that gives the best or close
-     to best results when tested on Cortex-M4 and Intel i7.
diff --git a/ChangeLog.d/x509-subaltname-ext b/ChangeLog.d/x509-subaltname-ext
deleted file mode 100644
index 7845f18..0000000
--- a/ChangeLog.d/x509-subaltname-ext
+++ /dev/null
@@ -1,5 +0,0 @@
-Bugfix
-   * Fix parsing of X.509 SubjectAlternativeName extension. Previously,
-     malformed alternative name components were not caught during initial
-     certificate parsing, but only on subsequent calls to
-     mbedtls_x509_parse_subject_alt_name(). Fixes #2838.
diff --git a/SECURITY.md b/SECURITY.md
index 33bbc2f..732335b 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -18,3 +18,120 @@
 Only the maintained branches, as listed in [`BRANCHES.md`](BRANCHES.md),
 get security fixes.
 Users are urged to always use the latest version of a maintained branch.
+
+## Threat model
+
+We classify attacks based on the capabilities of the attacker.
+
+### Remote attacks
+
+In this section, we consider an attacker who can observe and modify data sent
+over the network. This includes observing the content and timing of individual
+packets, as well as suppressing or delaying legitimate messages, and injecting
+messages.
+
+Mbed TLS aims to fully protect against remote attacks and to enable the user
+application in providing full protection against remote attacks. Said
+protection is limited to providing security guarantees offered by the protocol
+being implemented. (For example Mbed TLS alone won't guarantee that the
+messages will arrive without delay, as the TLS protocol doesn't guarantee that
+either.)
+
+**Warning!** Block ciphers do not yet achieve full protection against attackers
+who can measure the timing of packets with sufficient precision. For details
+and workarounds see the [Block Ciphers](#block-ciphers) section.
+
+### Local attacks
+
+In this section, we consider an attacker who can run software on the same
+machine. The attacker has insufficient privileges to directly access Mbed TLS
+assets such as memory and files.
+
+#### Timing attacks
+
+The attacker is able to observe the timing of instructions executed by Mbed TLS
+by leveraging shared hardware that both Mbed TLS and the attacker have access
+to. Typical attack vectors include cache timings, memory bus contention and
+branch prediction.
+
+Mbed TLS provides limited protection against timing attacks. The cost of
+protecting against timing attacks widely varies depending on the granularity of
+the measurements and the noise present. Therefore the protection in Mbed TLS is
+limited. We are only aiming to provide protection against **publicly
+documented attack techniques**.
+
+As attacks keep improving, so does Mbed TLS's protection. Mbed TLS is moving
+towards a model of fully timing-invariant code, but has not reached this point
+yet.
+
+**Remark:** Timing information can be observed over the network or through
+physical side channels as well. Remote and physical timing attacks are covered
+in the [Remote attacks](remote-attacks) and [Physical
+attacks](physical-attacks) sections respectively.
+
+**Warning!** Block ciphers do not yet achieve full protection. For
+details and workarounds see the [Block Ciphers](#block-ciphers) section.
+
+#### Local non-timing side channels
+
+The attacker code running on the platform has access to some sensor capable of
+picking up information on the physical state of the hardware while Mbed TLS is
+running. This could for example be an analogue-to-digital converter on the
+platform that is located unfortunately enough to pick up the CPU noise.
+
+Mbed TLS doesn't make any security guarantees against local non-timing-based
+side channel attacks. If local non-timing attacks are present in a use case or
+a user application's threat model, they need to be mitigated by the platform.
+
+#### Local fault injection attacks
+
+Software running on the same hardware can affect the physical state of the
+device and introduce faults.
+
+Mbed TLS doesn't make any security guarantees against local fault injection
+attacks. If local fault injection attacks are present in a use case or a user
+application's threat model, they need to be mitigated by the platform.
+
+### Physical attacks
+
+In this section, we consider an attacker who has access to physical information
+about the hardware Mbed TLS is running on and/or can alter the physical state
+of the hardware (e.g. power analysis, radio emissions or fault injection).
+
+Mbed TLS doesn't make any security guarantees against physical attacks. If
+physical attacks are present in a use case or a user application's threat
+model, they need to be mitigated by physical countermeasures.
+
+### Caveats
+
+#### Out-of-scope countermeasures
+
+Mbed TLS has evolved organically and a well defined threat model hasn't always
+been present. Therefore, Mbed TLS might have countermeasures against attacks
+outside the above defined threat model.
+
+The presence of such countermeasures don't mean that Mbed TLS provides
+protection against a class of attacks outside of the above described threat
+model. Neither does it mean that the failure of such a countermeasure is
+considered a vulnerability.
+
+#### Block ciphers
+
+Currently there are four block ciphers in Mbed TLS: AES, CAMELLIA, ARIA and
+DES. The pure software implementation in Mbed TLS implementation uses lookup
+tables, which are vulnerable to timing attacks.
+
+These timing attacks can be physical, local or depending on network latency
+even a remote. The attacks can result in key recovery.
+
+**Workarounds:**
+
+- Turn on hardware acceleration for AES. This is supported only on selected
+  architectures and currently only available for AES. See configuration options
+  `MBEDTLS_AESNI_C` and `MBEDTLS_PADLOCK_C` for details.
+- Add a secure alternative implementation (typically hardware acceleration) for
+  the vulnerable cipher. See the [Alternative Implementations
+Guide](docs/architecture/alternative-implementations.md) for more information.
+- Use cryptographic mechanisms that are not based on block ciphers. In
+  particular, for authenticated encryption, use ChaCha20/Poly1305 instead of
+  block cipher modes. For random generation, use HMAC\_DRBG instead of CTR\_DRBG.
diff --git a/docs/.gitignore b/docs/.gitignore
index 23f832b..11f197b 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -1,2 +1,4 @@
 *.html
 *.pdf
+_build/
+api/
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..47510f9
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,40 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help clean apidoc breathe_apidoc Makefile
+
+# Intercept the 'clean' target so we can do the right thing for apidoc as well
+clean:
+	@# Clean the apidoc
+	$(MAKE) -C .. apidoc_clean
+	@# Clean the breathe-apidoc generated files
+	rm -rf ./api
+	@# Clean the sphinx docs
+	@$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+apidoc:
+	@# Generate doxygen from source using the main Makefile
+	$(MAKE) -C .. apidoc
+
+breathe_apidoc: apidoc
+	@# Remove existing files - breathe-apidoc skips them if they're present
+	rm -rf ./api
+	@# Generate RST file structure with breathe-apidoc
+	breathe-apidoc -o ./api ../apidoc/xml
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile breathe_apidoc
+	@# Build the relevant target with sphinx
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..41c50c7
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,34 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+import glob
+
+project = 'Mbed TLS Versioned'
+copyright = '2023, Mbed TLS Contributors'
+author = 'Mbed TLS Contributors'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = ['breathe', 'sphinx.ext.graphviz']
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+breathe_projects = {
+    'mbedtls-versioned': '../apidoc/xml'
+}
+breathe_default_project = 'mbedtls-versioned'
+
+primary_domain = 'c'
+highlight_language = 'c'
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'sphinx_rtd_theme'
+html_static_path = ['_static']
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..33a9722
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,20 @@
+.. Mbed TLS Versioned documentation master file, created by
+   sphinx-quickstart on Thu Feb 23 18:13:44 2023.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Mbed TLS API documentation
+==========================
+
+.. doxygenpage:: index
+   :project: mbedtls-versioned
+
+.. toctree::
+   :caption: Contents
+   :maxdepth: 1
+
+   Home <self>
+   api/grouplist.rst
+   api/filelist.rst
+   api/structlist.rst
+   api/unionlist.rst
diff --git a/docs/requirements.in b/docs/requirements.in
new file mode 100644
index 0000000..a523188
--- /dev/null
+++ b/docs/requirements.in
@@ -0,0 +1,2 @@
+sphinx-rtd-theme
+breathe
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..4b9f3a6
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,66 @@
+#
+# This file is autogenerated by pip-compile with Python 3.9
+# by the following command:
+#
+#    pip-compile requirements.in
+#
+alabaster==0.7.13
+    # via sphinx
+babel==2.12.1
+    # via sphinx
+breathe==4.35.0
+    # via -r requirements.in
+certifi==2022.12.7
+    # via requests
+charset-normalizer==3.1.0
+    # via requests
+docutils==0.17.1
+    # via
+    #   breathe
+    #   sphinx
+    #   sphinx-rtd-theme
+idna==3.4
+    # via requests
+imagesize==1.4.1
+    # via sphinx
+importlib-metadata==6.0.0
+    # via sphinx
+jinja2==3.1.2
+    # via sphinx
+markupsafe==2.1.2
+    # via jinja2
+packaging==23.0
+    # via sphinx
+pygments==2.14.0
+    # via sphinx
+requests==2.28.2
+    # via sphinx
+snowballstemmer==2.2.0
+    # via sphinx
+sphinx==4.5.0
+    # via
+    #   breathe
+    #   sphinx-rtd-theme
+sphinx-rtd-theme==1.2.0
+    # via -r requirements.in
+sphinxcontrib-applehelp==1.0.4
+    # via sphinx
+sphinxcontrib-devhelp==1.0.2
+    # via sphinx
+sphinxcontrib-htmlhelp==2.0.1
+    # via sphinx
+sphinxcontrib-jquery==2.0.0
+    # via sphinx-rtd-theme
+sphinxcontrib-jsmath==1.0.1
+    # via sphinx
+sphinxcontrib-qthelp==1.0.3
+    # via sphinx
+sphinxcontrib-serializinghtml==1.1.5
+    # via sphinx
+urllib3==1.26.15
+    # via requests
+zipp==3.15.0
+    # via importlib-metadata
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/doxygen/input/doc_mainpage.h b/doxygen/input/doc_mainpage.h
index 54b2f7a..4c76246 100644
--- a/doxygen/input/doc_mainpage.h
+++ b/doxygen/input/doc_mainpage.h
@@ -22,7 +22,7 @@
  */
 
 /**
- * @mainpage mbed TLS v2.28.2 source code documentation
+ * @mainpage mbed TLS v2.28.3 source code documentation
  *
  * This documentation describes the internal structure of mbed TLS.  It was
  * automatically generated from specially formatted comment blocks in
diff --git a/doxygen/mbedtls.doxyfile b/doxygen/mbedtls.doxyfile
index 1f1a9cd..78299ed 100644
--- a/doxygen/mbedtls.doxyfile
+++ b/doxygen/mbedtls.doxyfile
@@ -1,4 +1,4 @@
-PROJECT_NAME           = "mbed TLS v2.28.2"
+PROJECT_NAME           = "mbed TLS v2.28.3"
 OUTPUT_DIRECTORY       = ../apidoc/
 FULL_PATH_NAMES        = NO
 OPTIMIZE_OUTPUT_FOR_C  = YES
@@ -19,6 +19,9 @@
 HTML_TIMESTAMP         = YES
 SEARCHENGINE           = YES
 GENERATE_LATEX         = NO
+GENERATE_XML           = YES
+MACRO_EXPANSION        = YES
+EXPAND_ONLY_PREDEF     = YES
 INCLUDE_PATH           = ../include
 CLASS_DIAGRAMS         = NO
 HAVE_DOT               = YES
@@ -40,3 +43,12 @@
 #     \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
 # This avoids writing redundant text and keeps Clang happy.
 ALIASES += emptydescription=""
+
+# Define away Mbed TLS macros that make parsing definitions difficult.
+# MBEDTLS_DEPRECATED is not included in this list as it's important to
+# display deprecated status in the documentation.
+PREDEFINED             = "MBEDTLS_CHECK_RETURN_CRITICAL="   \
+                         "MBEDTLS_CHECK_RETURN_TYPICAL="    \
+                         "MBEDTLS_CHECK_RETURN_OPTIONAL="   \
+                         "MBEDTLS_PRINTF_ATTRIBUTE(a,b)="   \
+
diff --git a/include/mbedtls/aesni.h b/include/mbedtls/aesni.h
index 653b146..6741dea 100644
--- a/include/mbedtls/aesni.h
+++ b/include/mbedtls/aesni.h
@@ -36,13 +36,49 @@
 #define MBEDTLS_AESNI_AES      0x02000000u
 #define MBEDTLS_AESNI_CLMUL    0x00000002u
 
-#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) &&  \
+/* Can we do AESNI with inline assembly?
+ * (Only implemented with gas syntax, only for 64-bit.)
+ */
+#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) && \
     (defined(__amd64__) || defined(__x86_64__))   &&  \
     !defined(MBEDTLS_HAVE_X86_64)
 #define MBEDTLS_HAVE_X86_64
 #endif
 
+#if defined(MBEDTLS_AESNI_C)
+
+/* Can we do AESNI with intrinsics?
+ * (Only implemented with certain compilers, only for certain targets.)
+ *
+ * NOTE: MBEDTLS_AESNI_HAVE_INTRINSICS and MBEDTLS_AESNI_HAVE_CODE are internal
+ *       macros that may change in future releases.
+ */
+#undef MBEDTLS_AESNI_HAVE_INTRINSICS
+#if defined(_MSC_VER)
+/* Visual Studio supports AESNI intrinsics since VS 2008 SP1. We only support
+ * VS 2013 and up for other reasons anyway, so no need to check the version. */
+#define MBEDTLS_AESNI_HAVE_INTRINSICS
+#endif
+/* GCC-like compilers: currently, we only support intrinsics if the requisite
+ * target flag is enabled when building the library (e.g. `gcc -mpclmul -msse2`
+ * or `clang -maes -mpclmul`). */
+#if defined(__GNUC__) && defined(__AES__) && defined(__PCLMUL__)
+#define MBEDTLS_AESNI_HAVE_INTRINSICS
+#endif
+
+/* Choose the implementation of AESNI, if one is available. */
+#undef MBEDTLS_AESNI_HAVE_CODE
+/* To minimize disruption when releasing the intrinsics-based implementation,
+ * favor the assembly-based implementation if it's available. We intend to
+ * revise this in a later release of Mbed TLS 3.x. In the long run, we will
+ * likely remove the assembly implementation. */
 #if defined(MBEDTLS_HAVE_X86_64)
+#define MBEDTLS_AESNI_HAVE_CODE 1 // via assembly
+#elif defined(MBEDTLS_AESNI_HAVE_INTRINSICS)
+#define MBEDTLS_AESNI_HAVE_CODE 2 // via intrinsics
+#endif
+
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
 
 #ifdef __cplusplus
 extern "C" {
@@ -131,6 +167,7 @@
 }
 #endif
 
-#endif /* MBEDTLS_HAVE_X86_64 */
+#endif /* MBEDTLS_AESNI_HAVE_CODE */
+#endif  /* MBEDTLS_AESNI_C */
 
 #endif /* MBEDTLS_AESNI_H */
diff --git a/include/mbedtls/bignum.h b/include/mbedtls/bignum.h
index 788ea21..f66379d 100644
--- a/include/mbedtls/bignum.h
+++ b/include/mbedtls/bignum.h
@@ -1039,7 +1039,7 @@
  *                 This must point to an initialized MPI.
  * \param rounds   The number of bases to perform the Miller-Rabin primality
  *                 test for. The probability of returning 0 on a composite is
- *                 at most 2<sup>-2*\p rounds</sup>.
+ *                 at most 2<sup>-2*\p rounds </sup>.
  * \param f_rng    The RNG function to use. This must not be \c NULL.
  * \param p_rng    The RNG parameter to be passed to \p f_rng.
  *                 This may be \c NULL if \p f_rng doesn't use
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index 2ab9982..2cb36e9 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -69,10 +69,6 @@
 #error "MBEDTLS_HAVE_TIME_DATE without MBEDTLS_HAVE_TIME does not make sense"
 #endif
 
-#if defined(MBEDTLS_AESNI_C) && !defined(MBEDTLS_HAVE_ASM)
-#error "MBEDTLS_AESNI_C defined, but not all prerequisites"
-#endif
-
 #if defined(MBEDTLS_CTR_DRBG_C) && !defined(MBEDTLS_AES_C)
 #error "MBEDTLS_CTR_DRBG_C defined, but not all prerequisites"
 #endif
diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index acdb7ac..1381c1f 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -51,7 +51,7 @@
  *      include/mbedtls/bn_mul.h
  *
  * Required by:
- *      MBEDTLS_AESNI_C
+ *      MBEDTLS_AESNI_C (on some platforms)
  *      MBEDTLS_PADLOCK_C
  *
  * Comment to disable the use of assembly code.
@@ -2344,14 +2344,32 @@
 /**
  * \def MBEDTLS_AESNI_C
  *
- * Enable AES-NI support on x86-64.
+ * Enable AES-NI support on x86-64 or x86-32.
+ *
+ * \note AESNI is only supported with certain compilers and target options:
+ * - Visual Studio 2013: supported.
+ * - GCC, x86-64, target not explicitly supporting AESNI:
+ *   requires MBEDTLS_HAVE_ASM.
+ * - GCC, x86-32, target not explicitly supporting AESNI:
+ *   not supported.
+ * - GCC, x86-64 or x86-32, target supporting AESNI: supported.
+ *   For this assembly-less implementation, you must currently compile
+ *   `library/aesni.c` and `library/aes.c` with machine options to enable
+ *   SSE2 and AESNI instructions: `gcc -msse2 -maes -mpclmul` or
+ *   `clang -maes -mpclmul`.
+ * - Non-x86 targets: this option is silently ignored.
+ * - Other compilers: this option is silently ignored.
+ *
+ * \note
+ * Above, "GCC" includes compatible compilers such as Clang.
+ * The limitations on target support are likely to be relaxed in the future.
  *
  * Module:  library/aesni.c
  * Caller:  library/aes.c
  *
- * Requires: MBEDTLS_HAVE_ASM
+ * Requires: MBEDTLS_HAVE_ASM (on some platforms, see note)
  *
- * This modules adds support for the AES-NI instructions on x86-64
+ * This modules adds support for the AES-NI instructions on x86.
  */
 #define MBEDTLS_AESNI_C
 
diff --git a/include/mbedtls/version.h b/include/mbedtls/version.h
index 1324eae..0ef5213 100644
--- a/include/mbedtls/version.h
+++ b/include/mbedtls/version.h
@@ -38,16 +38,16 @@
  */
 #define MBEDTLS_VERSION_MAJOR  2
 #define MBEDTLS_VERSION_MINOR  28
-#define MBEDTLS_VERSION_PATCH  2
+#define MBEDTLS_VERSION_PATCH  3
 
 /**
  * The single version number has the following structure:
  *    MMNNPP00
  *    Major version | Minor version | Patch version
  */
-#define MBEDTLS_VERSION_NUMBER         0x021C0200
-#define MBEDTLS_VERSION_STRING         "2.28.2"
-#define MBEDTLS_VERSION_STRING_FULL    "mbed TLS 2.28.2"
+#define MBEDTLS_VERSION_NUMBER         0x021C0300
+#define MBEDTLS_VERSION_STRING         "2.28.3"
+#define MBEDTLS_VERSION_STRING_FULL    "mbed TLS 2.28.3"
 
 #if defined(MBEDTLS_VERSION_C)
 
diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt
index fd3a3e7..553569e 100644
--- a/library/CMakeLists.txt
+++ b/library/CMakeLists.txt
@@ -204,15 +204,15 @@
 if(USE_SHARED_MBEDTLS_LIBRARY)
     set(CMAKE_LIBRARY_PATH ${CMAKE_CURRENT_BINARY_DIR})
     add_library(${mbedcrypto_target} SHARED ${src_crypto})
-    set_target_properties(${mbedcrypto_target} PROPERTIES VERSION 2.28.2 SOVERSION 7)
+    set_target_properties(${mbedcrypto_target} PROPERTIES VERSION 2.28.3 SOVERSION 7)
     target_link_libraries(${mbedcrypto_target} PUBLIC ${libs})
 
     add_library(${mbedx509_target} SHARED ${src_x509})
-    set_target_properties(${mbedx509_target} PROPERTIES VERSION 2.28.2 SOVERSION 1)
+    set_target_properties(${mbedx509_target} PROPERTIES VERSION 2.28.3 SOVERSION 1)
     target_link_libraries(${mbedx509_target} PUBLIC ${libs} ${mbedcrypto_target})
 
     add_library(${mbedtls_target} SHARED ${src_tls})
-    set_target_properties(${mbedtls_target} PROPERTIES VERSION 2.28.2 SOVERSION 14)
+    set_target_properties(${mbedtls_target} PROPERTIES VERSION 2.28.3 SOVERSION 14)
     target_link_libraries(${mbedtls_target} PUBLIC ${libs} ${mbedx509_target})
 endif(USE_SHARED_MBEDTLS_LIBRARY)
 
diff --git a/library/aes.c b/library/aes.c
index bcdf3c7..f08a21f 100644
--- a/library/aes.c
+++ b/library/aes.c
@@ -50,8 +50,7 @@
 #define AES_VALIDATE(cond)        \
     MBEDTLS_INTERNAL_VALIDATE(cond)
 
-#if defined(MBEDTLS_PADLOCK_C) &&                      \
-    (defined(MBEDTLS_HAVE_X86) || defined(MBEDTLS_PADLOCK_ALIGN16))
+#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86)
 static int aes_padlock_ace = -1;
 #endif
 
@@ -512,6 +511,53 @@
 }
 #endif /* MBEDTLS_CIPHER_MODE_XTS */
 
+/* Some implementations need the round keys to be aligned.
+ * Return an offset to be added to buf, such that (buf + offset) is
+ * correctly aligned.
+ * Note that the offset is in units of elements of buf, i.e. 32-bit words,
+ * i.e. an offset of 1 means 4 bytes and so on.
+ */
+#if (defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86)) ||        \
+    (defined(MBEDTLS_AESNI_C) && MBEDTLS_AESNI_HAVE_CODE == 2)
+#define MAY_NEED_TO_ALIGN
+#endif
+static unsigned mbedtls_aes_rk_offset(uint32_t *buf)
+{
+#if defined(MAY_NEED_TO_ALIGN)
+    int align_16_bytes = 0;
+
+#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86)
+    if (aes_padlock_ace == -1) {
+        aes_padlock_ace = mbedtls_padlock_has_support(MBEDTLS_PADLOCK_ACE);
+    }
+    if (aes_padlock_ace) {
+        align_16_bytes = 1;
+    }
+#endif
+
+#if defined(MBEDTLS_AESNI_C) && MBEDTLS_AESNI_HAVE_CODE == 2
+    if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) {
+        align_16_bytes = 1;
+    }
+#endif
+
+    if (align_16_bytes) {
+        /* These implementations needs 16-byte alignment
+         * for the round key array. */
+        unsigned delta = ((uintptr_t) buf & 0x0000000fU) / 4;
+        if (delta == 0) {
+            return 0;
+        } else {
+            return 4 - delta; // 16 bytes = 4 uint32_t
+        }
+    }
+#else /* MAY_NEED_TO_ALIGN */
+    (void) buf;
+#endif /* MAY_NEED_TO_ALIGN */
+
+    return 0;
+}
+
 /*
  * AES key schedule (encryption)
  */
@@ -539,18 +585,9 @@
     }
 #endif
 
-#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_PADLOCK_ALIGN16)
-    if (aes_padlock_ace == -1) {
-        aes_padlock_ace = mbedtls_padlock_has_support(MBEDTLS_PADLOCK_ACE);
-    }
+    ctx->rk = RK = ctx->buf + mbedtls_aes_rk_offset(ctx->buf);
 
-    if (aes_padlock_ace) {
-        ctx->rk = RK = MBEDTLS_PADLOCK_ALIGN16(ctx->buf);
-    } else
-#endif
-    ctx->rk = RK = ctx->buf;
-
-#if defined(MBEDTLS_AESNI_C) && defined(MBEDTLS_HAVE_X86_64)
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
     if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) {
         return mbedtls_aesni_setkey_enc((unsigned char *) ctx->rk, key, keybits);
     }
@@ -640,16 +677,7 @@
 
     mbedtls_aes_init(&cty);
 
-#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_PADLOCK_ALIGN16)
-    if (aes_padlock_ace == -1) {
-        aes_padlock_ace = mbedtls_padlock_has_support(MBEDTLS_PADLOCK_ACE);
-    }
-
-    if (aes_padlock_ace) {
-        ctx->rk = RK = MBEDTLS_PADLOCK_ALIGN16(ctx->buf);
-    } else
-#endif
-    ctx->rk = RK = ctx->buf;
+    ctx->rk = RK = ctx->buf + mbedtls_aes_rk_offset(ctx->buf);
 
     /* Also checks keybits */
     if ((ret = mbedtls_aes_setkey_enc(&cty, key, keybits)) != 0) {
@@ -658,7 +686,7 @@
 
     ctx->nr = cty.nr;
 
-#if defined(MBEDTLS_AESNI_C) && defined(MBEDTLS_HAVE_X86_64)
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
     if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) {
         mbedtls_aesni_inverse_key((unsigned char *) ctx->rk,
                                   (const unsigned char *) cty.rk, ctx->nr);
@@ -964,6 +992,30 @@
 }
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 
+#if defined(MAY_NEED_TO_ALIGN)
+/* VIA Padlock and our intrinsics-based implementation of AESNI require
+ * the round keys to be aligned on a 16-byte boundary. We take care of this
+ * before creating them, but the AES context may have moved (this can happen
+ * if the library is called from a language with managed memory), and in later
+ * calls it might have a different alignment with respect to 16-byte memory.
+ * So we may need to realign.
+ * NOTE: In the LTS branch, the context contains a pointer to within itself,
+ * so if it has been moved, things will probably go pear-shaped. We keep this
+ * code for compatibility with the development branch, in case of future changes.
+ */
+static void aes_maybe_realign(mbedtls_aes_context *ctx)
+{
+    unsigned current_offset = (unsigned) (ctx->rk - ctx->buf);
+    unsigned new_offset = mbedtls_aes_rk_offset(ctx->buf);
+    if (new_offset != current_offset) {
+        memmove(ctx->buf + new_offset,     // new address
+                ctx->buf + current_offset, // current address
+                (ctx->nr + 1) * 16);       // number of round keys * bytes per rk
+        ctx->rk = ctx->buf + new_offset;
+    }
+}
+#endif
+
 /*
  * AES-ECB block encryption/decryption
  */
@@ -978,7 +1030,11 @@
     AES_VALIDATE_RET(mode == MBEDTLS_AES_ENCRYPT ||
                      mode == MBEDTLS_AES_DECRYPT);
 
-#if defined(MBEDTLS_AESNI_C) && defined(MBEDTLS_HAVE_X86_64)
+#if defined(MAY_NEED_TO_ALIGN)
+    aes_maybe_realign(ctx);
+#endif
+
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
     if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) {
         return mbedtls_aesni_crypt_ecb(ctx, mode, input, output);
     }
@@ -986,13 +1042,7 @@
 
 #if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86)
     if (aes_padlock_ace) {
-        if (mbedtls_padlock_xcryptecb(ctx, mode, input, output) == 0) {
-            return 0;
-        }
-
-        // If padlock data misaligned, we just fall back to
-        // unaccelerated mode
-        //
+        return mbedtls_padlock_xcryptecb(ctx, mode, input, output);
     }
 #endif
 
@@ -1785,6 +1835,32 @@
     memset(key, 0, 32);
     mbedtls_aes_init(&ctx);
 
+    if (verbose != 0) {
+#if defined(MBEDTLS_AES_ALT)
+        mbedtls_printf("  AES note: alternative implementation.\n");
+#else /* MBEDTLS_AES_ALT */
+#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86)
+        if (mbedtls_padlock_has_support(MBEDTLS_PADLOCK_ACE)) {
+            mbedtls_printf("  AES note: using VIA Padlock.\n");
+        } else
+#endif
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
+        if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) {
+            mbedtls_printf("  AES note: using AESNI via ");
+#if MBEDTLS_AESNI_HAVE_CODE == 1
+            mbedtls_printf("assembly");
+#elif MBEDTLS_AESNI_HAVE_CODE == 2
+            mbedtls_printf("intrinsics");
+#else
+            mbedtls_printf("(unknown)");
+#endif
+            mbedtls_printf(".\n");
+        } else
+#endif
+        mbedtls_printf("  AES note: built-in implementation.\n");
+#endif /* MBEDTLS_AES_ALT */
+    }
+
     /*
      * ECB mode
      */
diff --git a/library/aesni.c b/library/aesni.c
index 2a44b0e..c909f65 100644
--- a/library/aesni.c
+++ b/library/aesni.c
@@ -18,21 +18,14 @@
  */
 
 /*
- * [AES-WP] http://software.intel.com/en-us/articles/intel-advanced-encryption-standard-aes-instructions-set
- * [CLMUL-WP] http://software.intel.com/en-us/articles/intel-carry-less-multiplication-instruction-and-its-usage-for-computing-the-gcm-mode/
+ * [AES-WP] https://www.intel.com/content/www/us/en/developer/articles/tool/intel-advanced-encryption-standard-aes-instructions-set.html
+ * [CLMUL-WP] https://www.intel.com/content/www/us/en/develop/download/intel-carry-less-multiplication-instruction-and-its-usage-for-computing-the-gcm-mode.html
  */
 
 #include "common.h"
 
 #if defined(MBEDTLS_AESNI_C)
 
-#if defined(__has_feature)
-#if __has_feature(memory_sanitizer)
-#warning \
-    "MBEDTLS_AESNI_C is known to cause spurious error reports with some memory sanitizers as they do not understand the assembly code."
-#endif
-#endif
-
 #include "mbedtls/aesni.h"
 
 #include <string.h>
@@ -43,7 +36,14 @@
 #endif
 /* *INDENT-ON* */
 
-#if defined(MBEDTLS_HAVE_X86_64)
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
+
+#if MBEDTLS_AESNI_HAVE_CODE == 2
+#if !defined(_WIN32)
+#include <cpuid.h>
+#endif
+#include <immintrin.h>
+#endif
 
 /*
  * AES-NI support detection routine
@@ -54,17 +54,355 @@
     static unsigned int c = 0;
 
     if (!done) {
+#if MBEDTLS_AESNI_HAVE_CODE == 2
+        static unsigned info[4] = { 0, 0, 0, 0 };
+#if defined(_MSC_VER)
+        __cpuid(info, 1);
+#else
+        __cpuid(1, info[0], info[1], info[2], info[3]);
+#endif
+        c = info[2];
+#else /* AESNI using asm */
         asm ("movl  $1, %%eax   \n\t"
              "cpuid             \n\t"
              : "=c" (c)
              :
              : "eax", "ebx", "edx");
+#endif /* MBEDTLS_AESNI_HAVE_CODE */
         done = 1;
     }
 
     return (c & what) != 0;
 }
 
+#if MBEDTLS_AESNI_HAVE_CODE == 2
+
+/*
+ * AES-NI AES-ECB block en(de)cryption
+ */
+int mbedtls_aesni_crypt_ecb(mbedtls_aes_context *ctx,
+                            int mode,
+                            const unsigned char input[16],
+                            unsigned char output[16])
+{
+    const __m128i *rk = (const __m128i *) (ctx->rk);
+    unsigned nr = ctx->nr; // Number of remaining rounds
+
+    // Load round key 0
+    __m128i state;
+    memcpy(&state, input, 16);
+    state = _mm_xor_si128(state, rk[0]);  // state ^= *rk;
+    ++rk;
+    --nr;
+
+    if (mode == 0) {
+        while (nr != 0) {
+            state = _mm_aesdec_si128(state, *rk);
+            ++rk;
+            --nr;
+        }
+        state = _mm_aesdeclast_si128(state, *rk);
+    } else {
+        while (nr != 0) {
+            state = _mm_aesenc_si128(state, *rk);
+            ++rk;
+            --nr;
+        }
+        state = _mm_aesenclast_si128(state, *rk);
+    }
+
+    memcpy(output, &state, 16);
+    return 0;
+}
+
+/*
+ * GCM multiplication: c = a times b in GF(2^128)
+ * Based on [CLMUL-WP] algorithms 1 (with equation 27) and 5.
+ */
+
+static void gcm_clmul(const __m128i aa, const __m128i bb,
+                      __m128i *cc, __m128i *dd)
+{
+    /*
+     * Caryless multiplication dd:cc = aa * bb
+     * using [CLMUL-WP] algorithm 1 (p. 12).
+     */
+    *cc = _mm_clmulepi64_si128(aa, bb, 0x00); // a0*b0 = c1:c0
+    *dd = _mm_clmulepi64_si128(aa, bb, 0x11); // a1*b1 = d1:d0
+    __m128i ee = _mm_clmulepi64_si128(aa, bb, 0x10); // a0*b1 = e1:e0
+    __m128i ff = _mm_clmulepi64_si128(aa, bb, 0x01); // a1*b0 = f1:f0
+    ff = _mm_xor_si128(ff, ee);                      // e1+f1:e0+f0
+    ee = ff;                                         // e1+f1:e0+f0
+    ff = _mm_srli_si128(ff, 8);                      // 0:e1+f1
+    ee = _mm_slli_si128(ee, 8);                      // e0+f0:0
+    *dd = _mm_xor_si128(*dd, ff);                    // d1:d0+e1+f1
+    *cc = _mm_xor_si128(*cc, ee);                    // c1+e0+f0:c0
+}
+
+static void gcm_shift(__m128i *cc, __m128i *dd)
+{
+    /* [CMUCL-WP] Algorithm 5 Step 1: shift cc:dd one bit to the left,
+     * taking advantage of [CLMUL-WP] eq 27 (p. 18). */
+    //                                        // *cc = r1:r0
+    //                                        // *dd = r3:r2
+    __m128i cc_lo = _mm_slli_epi64(*cc, 1);   // r1<<1:r0<<1
+    __m128i dd_lo = _mm_slli_epi64(*dd, 1);   // r3<<1:r2<<1
+    __m128i cc_hi = _mm_srli_epi64(*cc, 63);  // r1>>63:r0>>63
+    __m128i dd_hi = _mm_srli_epi64(*dd, 63);  // r3>>63:r2>>63
+    __m128i xmm5 = _mm_srli_si128(cc_hi, 8);  // 0:r1>>63
+    cc_hi = _mm_slli_si128(cc_hi, 8);         // r0>>63:0
+    dd_hi = _mm_slli_si128(dd_hi, 8);         // 0:r1>>63
+
+    *cc = _mm_or_si128(cc_lo, cc_hi);         // r1<<1|r0>>63:r0<<1
+    *dd = _mm_or_si128(_mm_or_si128(dd_lo, dd_hi), xmm5); // r3<<1|r2>>62:r2<<1|r1>>63
+}
+
+static __m128i gcm_reduce(__m128i xx)
+{
+    //                                            // xx = x1:x0
+    /* [CLMUL-WP] Algorithm 5 Step 2 */
+    __m128i aa = _mm_slli_epi64(xx, 63);          // x1<<63:x0<<63 = stuff:a
+    __m128i bb = _mm_slli_epi64(xx, 62);          // x1<<62:x0<<62 = stuff:b
+    __m128i cc = _mm_slli_epi64(xx, 57);          // x1<<57:x0<<57 = stuff:c
+    __m128i dd = _mm_slli_si128(_mm_xor_si128(_mm_xor_si128(aa, bb), cc), 8); // a+b+c:0
+    return _mm_xor_si128(dd, xx);                 // x1+a+b+c:x0 = d:x0
+}
+
+static __m128i gcm_mix(__m128i dx)
+{
+    /* [CLMUL-WP] Algorithm 5 Steps 3 and 4 */
+    __m128i ee = _mm_srli_epi64(dx, 1);           // e1:x0>>1 = e1:e0'
+    __m128i ff = _mm_srli_epi64(dx, 2);           // f1:x0>>2 = f1:f0'
+    __m128i gg = _mm_srli_epi64(dx, 7);           // g1:x0>>7 = g1:g0'
+
+    // e0'+f0'+g0' is almost e0+f0+g0, except for some missing
+    // bits carried from d. Now get those bits back in.
+    __m128i eh = _mm_slli_epi64(dx, 63);          // d<<63:stuff
+    __m128i fh = _mm_slli_epi64(dx, 62);          // d<<62:stuff
+    __m128i gh = _mm_slli_epi64(dx, 57);          // d<<57:stuff
+    __m128i hh = _mm_srli_si128(_mm_xor_si128(_mm_xor_si128(eh, fh), gh), 8); // 0:missing bits of d
+
+    return _mm_xor_si128(_mm_xor_si128(_mm_xor_si128(_mm_xor_si128(ee, ff), gg), hh), dx);
+}
+
+void mbedtls_aesni_gcm_mult(unsigned char c[16],
+                            const unsigned char a[16],
+                            const unsigned char b[16])
+{
+    __m128i aa, bb, cc, dd;
+
+    /* The inputs are in big-endian order, so byte-reverse them */
+    for (size_t i = 0; i < 16; i++) {
+        ((uint8_t *) &aa)[i] = a[15 - i];
+        ((uint8_t *) &bb)[i] = b[15 - i];
+    }
+
+    gcm_clmul(aa, bb, &cc, &dd);
+    gcm_shift(&cc, &dd);
+    /*
+     * Now reduce modulo the GCM polynomial x^128 + x^7 + x^2 + x + 1
+     * using [CLMUL-WP] algorithm 5 (p. 18).
+     * Currently dd:cc holds x3:x2:x1:x0 (already shifted).
+     */
+    __m128i dx = gcm_reduce(cc);
+    __m128i xh = gcm_mix(dx);
+    cc = _mm_xor_si128(xh, dd); // x3+h1:x2+h0
+
+    /* Now byte-reverse the outputs */
+    for (size_t i = 0; i < 16; i++) {
+        c[i] = ((uint8_t *) &cc)[15 - i];
+    }
+
+    return;
+}
+
+/*
+ * Compute decryption round keys from encryption round keys
+ */
+void mbedtls_aesni_inverse_key(unsigned char *invkey,
+                               const unsigned char *fwdkey, int nr)
+{
+    __m128i *ik = (__m128i *) invkey;
+    const __m128i *fk = (const __m128i *) fwdkey + nr;
+
+    *ik = *fk;
+    for (--fk, ++ik; fk > (const __m128i *) fwdkey; --fk, ++ik) {
+        *ik = _mm_aesimc_si128(*fk);
+    }
+    *ik = *fk;
+}
+
+/*
+ * Key expansion, 128-bit case
+ */
+static __m128i aesni_set_rk_128(__m128i state, __m128i xword)
+{
+    /*
+     * Finish generating the next round key.
+     *
+     * On entry state is r3:r2:r1:r0 and xword is X:stuff:stuff:stuff
+     * with X = rot( sub( r3 ) ) ^ RCON (obtained with AESKEYGENASSIST).
+     *
+     * On exit, xword is r7:r6:r5:r4
+     * with r4 = X + r0, r5 = r4 + r1, r6 = r5 + r2, r7 = r6 + r3
+     * and this is returned, to be written to the round key buffer.
+     */
+    xword = _mm_shuffle_epi32(xword, 0xff);   // X:X:X:X
+    xword = _mm_xor_si128(xword, state);      // X+r3:X+r2:X+r1:r4
+    state = _mm_slli_si128(state, 4);         // r2:r1:r0:0
+    xword = _mm_xor_si128(xword, state);      // X+r3+r2:X+r2+r1:r5:r4
+    state = _mm_slli_si128(state, 4);         // r1:r0:0:0
+    xword = _mm_xor_si128(xword, state);      // X+r3+r2+r1:r6:r5:r4
+    state = _mm_slli_si128(state, 4);         // r0:0:0:0
+    state = _mm_xor_si128(xword, state);      // r7:r6:r5:r4
+    return state;
+}
+
+static void aesni_setkey_enc_128(unsigned char *rk_bytes,
+                                 const unsigned char *key)
+{
+    __m128i *rk = (__m128i *) rk_bytes;
+
+    memcpy(&rk[0], key, 16);
+    rk[1] = aesni_set_rk_128(rk[0], _mm_aeskeygenassist_si128(rk[0], 0x01));
+    rk[2] = aesni_set_rk_128(rk[1], _mm_aeskeygenassist_si128(rk[1], 0x02));
+    rk[3] = aesni_set_rk_128(rk[2], _mm_aeskeygenassist_si128(rk[2], 0x04));
+    rk[4] = aesni_set_rk_128(rk[3], _mm_aeskeygenassist_si128(rk[3], 0x08));
+    rk[5] = aesni_set_rk_128(rk[4], _mm_aeskeygenassist_si128(rk[4], 0x10));
+    rk[6] = aesni_set_rk_128(rk[5], _mm_aeskeygenassist_si128(rk[5], 0x20));
+    rk[7] = aesni_set_rk_128(rk[6], _mm_aeskeygenassist_si128(rk[6], 0x40));
+    rk[8] = aesni_set_rk_128(rk[7], _mm_aeskeygenassist_si128(rk[7], 0x80));
+    rk[9] = aesni_set_rk_128(rk[8], _mm_aeskeygenassist_si128(rk[8], 0x1B));
+    rk[10] = aesni_set_rk_128(rk[9], _mm_aeskeygenassist_si128(rk[9], 0x36));
+}
+
+/*
+ * Key expansion, 192-bit case
+ */
+static void aesni_set_rk_192(__m128i *state0, __m128i *state1, __m128i xword,
+                             unsigned char *rk)
+{
+    /*
+     * Finish generating the next 6 quarter-keys.
+     *
+     * On entry state0 is r3:r2:r1:r0, state1 is stuff:stuff:r5:r4
+     * and xword is stuff:stuff:X:stuff with X = rot( sub( r3 ) ) ^ RCON
+     * (obtained with AESKEYGENASSIST).
+     *
+     * On exit, state0 is r9:r8:r7:r6 and state1 is stuff:stuff:r11:r10
+     * and those are written to the round key buffer.
+     */
+    xword = _mm_shuffle_epi32(xword, 0x55);   // X:X:X:X
+    xword = _mm_xor_si128(xword, *state0);    // X+r3:X+r2:X+r1:X+r0
+    *state0 = _mm_slli_si128(*state0, 4);     // r2:r1:r0:0
+    xword = _mm_xor_si128(xword, *state0);    // X+r3+r2:X+r2+r1:X+r1+r0:X+r0
+    *state0 = _mm_slli_si128(*state0, 4);     // r1:r0:0:0
+    xword = _mm_xor_si128(xword, *state0);    // X+r3+r2+r1:X+r2+r1+r0:X+r1+r0:X+r0
+    *state0 = _mm_slli_si128(*state0, 4);     // r0:0:0:0
+    xword = _mm_xor_si128(xword, *state0);    // X+r3+r2+r1+r0:X+r2+r1+r0:X+r1+r0:X+r0
+    *state0 = xword;                          // = r9:r8:r7:r6
+
+    xword = _mm_shuffle_epi32(xword, 0xff);   // r9:r9:r9:r9
+    xword = _mm_xor_si128(xword, *state1);    // stuff:stuff:r9+r5:r9+r4
+    *state1 = _mm_slli_si128(*state1, 4);     // stuff:stuff:r4:0
+    xword = _mm_xor_si128(xword, *state1);    // stuff:stuff:r9+r5+r4:r9+r4
+    *state1 = xword;                          // = stuff:stuff:r11:r10
+
+    /* Store state0 and the low half of state1 into rk, which is conceptually
+     * an array of 24-byte elements. Since 24 is not a multiple of 16,
+     * rk is not necessarily aligned so just `*rk = *state0` doesn't work. */
+    memcpy(rk, state0, 16);
+    memcpy(rk + 16, state1, 8);
+}
+
+static void aesni_setkey_enc_192(unsigned char *rk,
+                                 const unsigned char *key)
+{
+    /* First round: use original key */
+    memcpy(rk, key, 24);
+    /* aes.c guarantees that rk is aligned on a 16-byte boundary. */
+    __m128i state0 = ((__m128i *) rk)[0];
+    __m128i state1 = _mm_loadl_epi64(((__m128i *) rk) + 1);
+
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x01), rk + 24 * 1);
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x02), rk + 24 * 2);
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x04), rk + 24 * 3);
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x08), rk + 24 * 4);
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x10), rk + 24 * 5);
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x20), rk + 24 * 6);
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x40), rk + 24 * 7);
+    aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x80), rk + 24 * 8);
+}
+
+/*
+ * Key expansion, 256-bit case
+ */
+static void aesni_set_rk_256(__m128i state0, __m128i state1, __m128i xword,
+                             __m128i *rk0, __m128i *rk1)
+{
+    /*
+     * Finish generating the next two round keys.
+     *
+     * On entry state0 is r3:r2:r1:r0, state1 is r7:r6:r5:r4 and
+     * xword is X:stuff:stuff:stuff with X = rot( sub( r7 )) ^ RCON
+     * (obtained with AESKEYGENASSIST).
+     *
+     * On exit, *rk0 is r11:r10:r9:r8 and *rk1 is r15:r14:r13:r12
+     */
+    xword = _mm_shuffle_epi32(xword, 0xff);
+    xword = _mm_xor_si128(xword, state0);
+    state0 = _mm_slli_si128(state0, 4);
+    xword = _mm_xor_si128(xword, state0);
+    state0 = _mm_slli_si128(state0, 4);
+    xword = _mm_xor_si128(xword, state0);
+    state0 = _mm_slli_si128(state0, 4);
+    state0 = _mm_xor_si128(state0, xword);
+    *rk0 = state0;
+
+    /* Set xword to stuff:Y:stuff:stuff with Y = subword( r11 )
+     * and proceed to generate next round key from there */
+    xword = _mm_aeskeygenassist_si128(state0, 0x00);
+    xword = _mm_shuffle_epi32(xword, 0xaa);
+    xword = _mm_xor_si128(xword, state1);
+    state1 = _mm_slli_si128(state1, 4);
+    xword = _mm_xor_si128(xword, state1);
+    state1 = _mm_slli_si128(state1, 4);
+    xword = _mm_xor_si128(xword, state1);
+    state1 = _mm_slli_si128(state1, 4);
+    state1 = _mm_xor_si128(state1, xword);
+    *rk1 = state1;
+}
+
+static void aesni_setkey_enc_256(unsigned char *rk_bytes,
+                                 const unsigned char *key)
+{
+    __m128i *rk = (__m128i *) rk_bytes;
+
+    memcpy(&rk[0], key, 16);
+    memcpy(&rk[1], key + 16, 16);
+
+    /*
+     * Main "loop" - Generating one more key than necessary,
+     * see definition of mbedtls_aes_context.buf
+     */
+    aesni_set_rk_256(rk[0], rk[1], _mm_aeskeygenassist_si128(rk[1], 0x01), &rk[2], &rk[3]);
+    aesni_set_rk_256(rk[2], rk[3], _mm_aeskeygenassist_si128(rk[3], 0x02), &rk[4], &rk[5]);
+    aesni_set_rk_256(rk[4], rk[5], _mm_aeskeygenassist_si128(rk[5], 0x04), &rk[6], &rk[7]);
+    aesni_set_rk_256(rk[6], rk[7], _mm_aeskeygenassist_si128(rk[7], 0x08), &rk[8], &rk[9]);
+    aesni_set_rk_256(rk[8], rk[9], _mm_aeskeygenassist_si128(rk[9], 0x10), &rk[10], &rk[11]);
+    aesni_set_rk_256(rk[10], rk[11], _mm_aeskeygenassist_si128(rk[11], 0x20), &rk[12], &rk[13]);
+    aesni_set_rk_256(rk[12], rk[13], _mm_aeskeygenassist_si128(rk[13], 0x40), &rk[14], &rk[15]);
+}
+
+#else /* MBEDTLS_AESNI_HAVE_CODE == 1 */
+
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+#warning \
+    "MBEDTLS_AESNI_C is known to cause spurious error reports with some memory sanitizers as they do not understand the assembly code."
+#endif
+#endif
+
 /*
  * Binutils needs to be at least 2.19 to support AES-NI instructions.
  * Unfortunately, a lot of users have a lower version now (2014-04).
@@ -75,13 +413,13 @@
  * Operand macros are in gas order (src, dst) as opposed to Intel order
  * (dst, src) in order to blend better into the surrounding assembly code.
  */
-#define AESDEC      ".byte 0x66,0x0F,0x38,0xDE,"
-#define AESDECLAST  ".byte 0x66,0x0F,0x38,0xDF,"
-#define AESENC      ".byte 0x66,0x0F,0x38,0xDC,"
-#define AESENCLAST  ".byte 0x66,0x0F,0x38,0xDD,"
-#define AESIMC      ".byte 0x66,0x0F,0x38,0xDB,"
-#define AESKEYGENA  ".byte 0x66,0x0F,0x3A,0xDF,"
-#define PCLMULQDQ   ".byte 0x66,0x0F,0x3A,0x44,"
+#define AESDEC(regs)      ".byte 0x66,0x0F,0x38,0xDE," regs "\n\t"
+#define AESDECLAST(regs)  ".byte 0x66,0x0F,0x38,0xDF," regs "\n\t"
+#define AESENC(regs)      ".byte 0x66,0x0F,0x38,0xDC," regs "\n\t"
+#define AESENCLAST(regs)  ".byte 0x66,0x0F,0x38,0xDD," regs "\n\t"
+#define AESIMC(regs)      ".byte 0x66,0x0F,0x38,0xDB," regs "\n\t"
+#define AESKEYGENA(regs, imm)  ".byte 0x66,0x0F,0x3A,0xDF," regs "," imm "\n\t"
+#define PCLMULQDQ(regs, imm)   ".byte 0x66,0x0F,0x3A,0x44," regs "," imm "\n\t"
 
 #define xmm0_xmm0   "0xC0"
 #define xmm0_xmm1   "0xC8"
@@ -109,25 +447,25 @@
 
          "1:                        \n\t" // encryption loop
          "movdqu    (%1), %%xmm1    \n\t" // load round key
-         AESENC     xmm1_xmm0      "\n\t" // do round
-                                   "add       $16, %1         \n\t" // point to next round key
-                                   "subl      $1, %0          \n\t" // loop
-                                   "jnz       1b              \n\t"
-                                   "movdqu    (%1), %%xmm1    \n\t" // load round key
-         AESENCLAST xmm1_xmm0      "\n\t" // last round
-                                   "jmp       3f              \n\t"
+         AESENC(xmm1_xmm0)                // do round
+         "add       $16, %1         \n\t" // point to next round key
+         "subl      $1, %0          \n\t" // loop
+         "jnz       1b              \n\t"
+         "movdqu    (%1), %%xmm1    \n\t" // load round key
+         AESENCLAST(xmm1_xmm0)            // last round
+         "jmp       3f              \n\t"
 
-                                   "2:                        \n\t" // decryption loop
-                                   "movdqu    (%1), %%xmm1    \n\t"
-         AESDEC     xmm1_xmm0      "\n\t" // do round
-                                   "add       $16, %1         \n\t"
-                                   "subl      $1, %0          \n\t"
-                                   "jnz       2b              \n\t"
-                                   "movdqu    (%1), %%xmm1    \n\t" // load round key
-         AESDECLAST xmm1_xmm0      "\n\t" // last round
+         "2:                        \n\t" // decryption loop
+         "movdqu    (%1), %%xmm1    \n\t"
+         AESDEC(xmm1_xmm0)                // do round
+         "add       $16, %1         \n\t"
+         "subl      $1, %0          \n\t"
+         "jnz       2b              \n\t"
+         "movdqu    (%1), %%xmm1    \n\t" // load round key
+         AESDECLAST(xmm1_xmm0)            // last round
 
-                                   "3:                        \n\t"
-                                   "movdqu    %%xmm0, (%4)    \n\t" // export output
+         "3:                        \n\t"
+         "movdqu    %%xmm0, (%4)    \n\t" // export output
          :
          : "r" (ctx->nr), "r" (ctx->rk), "r" (mode), "r" (input), "r" (output)
          : "memory", "cc", "xmm0", "xmm1");
@@ -158,84 +496,84 @@
 
          /*
           * Caryless multiplication xmm2:xmm1 = xmm0 * xmm1
-          * using [CLMUL-WP] algorithm 1 (p. 13).
+          * using [CLMUL-WP] algorithm 1 (p. 12).
           */
          "movdqa %%xmm1, %%xmm2             \n\t" // copy of b1:b0
          "movdqa %%xmm1, %%xmm3             \n\t" // same
          "movdqa %%xmm1, %%xmm4             \n\t" // same
-         PCLMULQDQ xmm0_xmm1 ",0x00         \n\t" // a0*b0 = c1:c0
-         PCLMULQDQ xmm0_xmm2 ",0x11         \n\t" // a1*b1 = d1:d0
-         PCLMULQDQ xmm0_xmm3 ",0x10         \n\t" // a0*b1 = e1:e0
-         PCLMULQDQ xmm0_xmm4 ",0x01         \n\t" // a1*b0 = f1:f0
-                             "pxor %%xmm3, %%xmm4               \n\t" // e1+f1:e0+f0
-                             "movdqa %%xmm4, %%xmm3             \n\t" // same
-                             "psrldq $8, %%xmm4                 \n\t" // 0:e1+f1
-                             "pslldq $8, %%xmm3                 \n\t" // e0+f0:0
-                             "pxor %%xmm4, %%xmm2               \n\t" // d1:d0+e1+f1
-                             "pxor %%xmm3, %%xmm1               \n\t" // c1+e0+f1:c0
+         PCLMULQDQ(xmm0_xmm1, "0x00")             // a0*b0 = c1:c0
+         PCLMULQDQ(xmm0_xmm2, "0x11")             // a1*b1 = d1:d0
+         PCLMULQDQ(xmm0_xmm3, "0x10")             // a0*b1 = e1:e0
+         PCLMULQDQ(xmm0_xmm4, "0x01")             // a1*b0 = f1:f0
+         "pxor %%xmm3, %%xmm4               \n\t" // e1+f1:e0+f0
+         "movdqa %%xmm4, %%xmm3             \n\t" // same
+         "psrldq $8, %%xmm4                 \n\t" // 0:e1+f1
+         "pslldq $8, %%xmm3                 \n\t" // e0+f0:0
+         "pxor %%xmm4, %%xmm2               \n\t" // d1:d0+e1+f1
+         "pxor %%xmm3, %%xmm1               \n\t" // c1+e0+f1:c0
 
          /*
           * Now shift the result one bit to the left,
-          * taking advantage of [CLMUL-WP] eq 27 (p. 20)
+          * taking advantage of [CLMUL-WP] eq 27 (p. 18)
           */
-                             "movdqa %%xmm1, %%xmm3             \n\t" // r1:r0
-                             "movdqa %%xmm2, %%xmm4             \n\t" // r3:r2
-                             "psllq $1, %%xmm1                  \n\t" // r1<<1:r0<<1
-                             "psllq $1, %%xmm2                  \n\t" // r3<<1:r2<<1
-                             "psrlq $63, %%xmm3                 \n\t" // r1>>63:r0>>63
-                             "psrlq $63, %%xmm4                 \n\t" // r3>>63:r2>>63
-                             "movdqa %%xmm3, %%xmm5             \n\t" // r1>>63:r0>>63
-                             "pslldq $8, %%xmm3                 \n\t" // r0>>63:0
-                             "pslldq $8, %%xmm4                 \n\t" // r2>>63:0
-                             "psrldq $8, %%xmm5                 \n\t" // 0:r1>>63
-                             "por %%xmm3, %%xmm1                \n\t" // r1<<1|r0>>63:r0<<1
-                             "por %%xmm4, %%xmm2                \n\t" // r3<<1|r2>>62:r2<<1
-                             "por %%xmm5, %%xmm2                \n\t" // r3<<1|r2>>62:r2<<1|r1>>63
+         "movdqa %%xmm1, %%xmm3             \n\t" // r1:r0
+         "movdqa %%xmm2, %%xmm4             \n\t" // r3:r2
+         "psllq $1, %%xmm1                  \n\t" // r1<<1:r0<<1
+         "psllq $1, %%xmm2                  \n\t" // r3<<1:r2<<1
+         "psrlq $63, %%xmm3                 \n\t" // r1>>63:r0>>63
+         "psrlq $63, %%xmm4                 \n\t" // r3>>63:r2>>63
+         "movdqa %%xmm3, %%xmm5             \n\t" // r1>>63:r0>>63
+         "pslldq $8, %%xmm3                 \n\t" // r0>>63:0
+         "pslldq $8, %%xmm4                 \n\t" // r2>>63:0
+         "psrldq $8, %%xmm5                 \n\t" // 0:r1>>63
+         "por %%xmm3, %%xmm1                \n\t" // r1<<1|r0>>63:r0<<1
+         "por %%xmm4, %%xmm2                \n\t" // r3<<1|r2>>62:r2<<1
+         "por %%xmm5, %%xmm2                \n\t" // r3<<1|r2>>62:r2<<1|r1>>63
 
          /*
           * Now reduce modulo the GCM polynomial x^128 + x^7 + x^2 + x + 1
-          * using [CLMUL-WP] algorithm 5 (p. 20).
+          * using [CLMUL-WP] algorithm 5 (p. 18).
           * Currently xmm2:xmm1 holds x3:x2:x1:x0 (already shifted).
           */
          /* Step 2 (1) */
-                             "movdqa %%xmm1, %%xmm3             \n\t" // x1:x0
-                             "movdqa %%xmm1, %%xmm4             \n\t" // same
-                             "movdqa %%xmm1, %%xmm5             \n\t" // same
-                             "psllq $63, %%xmm3                 \n\t" // x1<<63:x0<<63 = stuff:a
-                             "psllq $62, %%xmm4                 \n\t" // x1<<62:x0<<62 = stuff:b
-                             "psllq $57, %%xmm5                 \n\t" // x1<<57:x0<<57 = stuff:c
+         "movdqa %%xmm1, %%xmm3             \n\t" // x1:x0
+         "movdqa %%xmm1, %%xmm4             \n\t" // same
+         "movdqa %%xmm1, %%xmm5             \n\t" // same
+         "psllq $63, %%xmm3                 \n\t" // x1<<63:x0<<63 = stuff:a
+         "psllq $62, %%xmm4                 \n\t" // x1<<62:x0<<62 = stuff:b
+         "psllq $57, %%xmm5                 \n\t" // x1<<57:x0<<57 = stuff:c
 
          /* Step 2 (2) */
-                             "pxor %%xmm4, %%xmm3               \n\t" // stuff:a+b
-                             "pxor %%xmm5, %%xmm3               \n\t" // stuff:a+b+c
-                             "pslldq $8, %%xmm3                 \n\t" // a+b+c:0
-                             "pxor %%xmm3, %%xmm1               \n\t" // x1+a+b+c:x0 = d:x0
+         "pxor %%xmm4, %%xmm3               \n\t" // stuff:a+b
+         "pxor %%xmm5, %%xmm3               \n\t" // stuff:a+b+c
+         "pslldq $8, %%xmm3                 \n\t" // a+b+c:0
+         "pxor %%xmm3, %%xmm1               \n\t" // x1+a+b+c:x0 = d:x0
 
          /* Steps 3 and 4 */
-                             "movdqa %%xmm1,%%xmm0              \n\t" // d:x0
-                             "movdqa %%xmm1,%%xmm4              \n\t" // same
-                             "movdqa %%xmm1,%%xmm5              \n\t" // same
-                             "psrlq $1, %%xmm0                  \n\t" // e1:x0>>1 = e1:e0'
-                             "psrlq $2, %%xmm4                  \n\t" // f1:x0>>2 = f1:f0'
-                             "psrlq $7, %%xmm5                  \n\t" // g1:x0>>7 = g1:g0'
-                             "pxor %%xmm4, %%xmm0               \n\t" // e1+f1:e0'+f0'
-                             "pxor %%xmm5, %%xmm0               \n\t" // e1+f1+g1:e0'+f0'+g0'
+         "movdqa %%xmm1,%%xmm0              \n\t" // d:x0
+         "movdqa %%xmm1,%%xmm4              \n\t" // same
+         "movdqa %%xmm1,%%xmm5              \n\t" // same
+         "psrlq $1, %%xmm0                  \n\t" // e1:x0>>1 = e1:e0'
+         "psrlq $2, %%xmm4                  \n\t" // f1:x0>>2 = f1:f0'
+         "psrlq $7, %%xmm5                  \n\t" // g1:x0>>7 = g1:g0'
+         "pxor %%xmm4, %%xmm0               \n\t" // e1+f1:e0'+f0'
+         "pxor %%xmm5, %%xmm0               \n\t" // e1+f1+g1:e0'+f0'+g0'
          // e0'+f0'+g0' is almost e0+f0+g0, ex\tcept for some missing
          // bits carried from d. Now get those\t bits back in.
-                             "movdqa %%xmm1,%%xmm3              \n\t" // d:x0
-                             "movdqa %%xmm1,%%xmm4              \n\t" // same
-                             "movdqa %%xmm1,%%xmm5              \n\t" // same
-                             "psllq $63, %%xmm3                 \n\t" // d<<63:stuff
-                             "psllq $62, %%xmm4                 \n\t" // d<<62:stuff
-                             "psllq $57, %%xmm5                 \n\t" // d<<57:stuff
-                             "pxor %%xmm4, %%xmm3               \n\t" // d<<63+d<<62:stuff
-                             "pxor %%xmm5, %%xmm3               \n\t" // missing bits of d:stuff
-                             "psrldq $8, %%xmm3                 \n\t" // 0:missing bits of d
-                             "pxor %%xmm3, %%xmm0               \n\t" // e1+f1+g1:e0+f0+g0
-                             "pxor %%xmm1, %%xmm0               \n\t" // h1:h0
-                             "pxor %%xmm2, %%xmm0               \n\t" // x3+h1:x2+h0
+         "movdqa %%xmm1,%%xmm3              \n\t" // d:x0
+         "movdqa %%xmm1,%%xmm4              \n\t" // same
+         "movdqa %%xmm1,%%xmm5              \n\t" // same
+         "psllq $63, %%xmm3                 \n\t" // d<<63:stuff
+         "psllq $62, %%xmm4                 \n\t" // d<<62:stuff
+         "psllq $57, %%xmm5                 \n\t" // d<<57:stuff
+         "pxor %%xmm4, %%xmm3               \n\t" // d<<63+d<<62:stuff
+         "pxor %%xmm5, %%xmm3               \n\t" // missing bits of d:stuff
+         "psrldq $8, %%xmm3                 \n\t" // 0:missing bits of d
+         "pxor %%xmm3, %%xmm0               \n\t" // e1+f1+g1:e0+f0+g0
+         "pxor %%xmm1, %%xmm0               \n\t" // h1:h0
+         "pxor %%xmm2, %%xmm0               \n\t" // x3+h1:x2+h0
 
-                             "movdqu %%xmm0, (%2)               \n\t" // done
+         "movdqu %%xmm0, (%2)               \n\t" // done
          :
          : "r" (aa), "r" (bb), "r" (cc)
          : "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5");
@@ -261,8 +599,8 @@
 
     for (fk -= 16, ik += 16; fk > fwdkey; fk -= 16, ik += 16) {
         asm ("movdqu (%0), %%xmm0       \n\t"
-             AESIMC  xmm0_xmm0         "\n\t"
-                                       "movdqu %%xmm0, (%1)       \n\t"
+             AESIMC(xmm0_xmm0)
+             "movdqu %%xmm0, (%1)       \n\t"
              :
              : "r" (fk), "r" (ik)
              : "memory", "xmm0");
@@ -306,16 +644,16 @@
 
          /* Main "loop" */
          "2:                                \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x01        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x02        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x04        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x08        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x10        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x20        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x40        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x80        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x1B        \n\tcall 1b \n\t"
-         AESKEYGENA xmm0_xmm1 ",0x36        \n\tcall 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x01")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x02")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x04")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x08")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x10")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x20")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x40")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x80")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x1B")      "call 1b \n\t"
+         AESKEYGENA(xmm0_xmm1, "0x36")      "call 1b \n\t"
          :
          : "r" (rk), "r" (key)
          : "memory", "cc", "0");
@@ -364,14 +702,14 @@
          "ret                           \n\t"
 
          "2:                            \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x01    \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x02    \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x04    \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x08    \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x10    \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x20    \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x40    \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x80    \n\tcall 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x01")  "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x02")  "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x04")  "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x08")  "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x10")  "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x20")  "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x40")  "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x80")  "call 1b \n\t"
 
          :
          : "r" (rk), "r" (key)
@@ -414,36 +752,38 @@
 
          /* Set xmm2 to stuff:Y:stuff:stuff with Y = subword( r11 )
           * and proceed to generate next round key from there */
-         AESKEYGENA xmm0_xmm2 ",0x00        \n\t"
-                              "pshufd $0xaa, %%xmm2, %%xmm2      \n\t"
-                              "pxor %%xmm1, %%xmm2               \n\t"
-                              "pslldq $4, %%xmm1                 \n\t"
-                              "pxor %%xmm1, %%xmm2               \n\t"
-                              "pslldq $4, %%xmm1                 \n\t"
-                              "pxor %%xmm1, %%xmm2               \n\t"
-                              "pslldq $4, %%xmm1                 \n\t"
-                              "pxor %%xmm2, %%xmm1               \n\t"
-                              "add $16, %0                       \n\t"
-                              "movdqu %%xmm1, (%0)               \n\t"
-                              "ret                               \n\t"
+         AESKEYGENA(xmm0_xmm2, "0x00")
+         "pshufd $0xaa, %%xmm2, %%xmm2      \n\t"
+         "pxor %%xmm1, %%xmm2               \n\t"
+         "pslldq $4, %%xmm1                 \n\t"
+         "pxor %%xmm1, %%xmm2               \n\t"
+         "pslldq $4, %%xmm1                 \n\t"
+         "pxor %%xmm1, %%xmm2               \n\t"
+         "pslldq $4, %%xmm1                 \n\t"
+         "pxor %%xmm2, %%xmm1               \n\t"
+         "add $16, %0                       \n\t"
+         "movdqu %%xmm1, (%0)               \n\t"
+         "ret                               \n\t"
 
          /*
           * Main "loop" - Generating one more key than necessary,
           * see definition of mbedtls_aes_context.buf
           */
-                              "2:                                \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x01        \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x02        \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x04        \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x08        \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x10        \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x20        \n\tcall 1b \n\t"
-         AESKEYGENA xmm1_xmm2 ",0x40        \n\tcall 1b \n\t"
+         "2:                                \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x01")      "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x02")      "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x04")      "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x08")      "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x10")      "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x20")      "call 1b \n\t"
+         AESKEYGENA(xmm1_xmm2, "0x40")      "call 1b \n\t"
          :
          : "r" (rk), "r" (key)
          : "memory", "cc", "0");
 }
 
+#endif  /* MBEDTLS_AESNI_HAVE_CODE */
+
 /*
  * Key expansion, wrapper
  */
@@ -461,6 +801,6 @@
     return 0;
 }
 
-#endif /* MBEDTLS_HAVE_X86_64 */
+#endif /* MBEDTLS_AESNI_HAVE_CODE */
 
 #endif /* MBEDTLS_AESNI_C */
diff --git a/library/gcm.c b/library/gcm.c
index f7db0d4..0c958c7 100644
--- a/library/gcm.c
+++ b/library/gcm.c
@@ -93,7 +93,7 @@
     ctx->HL[8] = vl;
     ctx->HH[8] = vh;
 
-#if defined(MBEDTLS_AESNI_C) && defined(MBEDTLS_HAVE_X86_64)
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
     /* With CLMUL support, we need only h, not the rest of the table */
     if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) {
         return 0;
@@ -190,7 +190,7 @@
     unsigned char lo, hi, rem;
     uint64_t zh, zl;
 
-#if defined(MBEDTLS_AESNI_C) && defined(MBEDTLS_HAVE_X86_64)
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
     if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) {
         unsigned char h[16];
 
@@ -202,7 +202,7 @@
         mbedtls_aesni_gcm_mult(output, x, h);
         return;
     }
-#endif /* MBEDTLS_AESNI_C && MBEDTLS_HAVE_X86_64 */
+#endif /* MBEDTLS_AESNI_HAVE_CODE */
 
     lo = x[15] & 0xf;
 
@@ -754,6 +754,27 @@
     int i, j, ret;
     mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
 
+    if (verbose != 0) {
+#if defined(MBEDTLS_GCM_ALT)
+        mbedtls_printf("  GCM note: alternative implementation.\n");
+#else /* MBEDTLS_GCM_ALT */
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
+        if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) {
+            mbedtls_printf("  GCM note: using AESNI via ");
+#if MBEDTLS_AESNI_HAVE_CODE == 1
+            mbedtls_printf("assembly");
+#elif MBEDTLS_AESNI_HAVE_CODE == 2
+            mbedtls_printf("intrinsics");
+#else
+            mbedtls_printf("(unknown)");
+#endif
+            mbedtls_printf(".\n");
+        } else
+#endif
+        mbedtls_printf("  GCM note: built-in implementation.\n");
+#endif /* MBEDTLS_GCM_ALT */
+    }
+
     for (j = 0; j < 3; j++) {
         int key_len = 128 + 64 * j;
 
diff --git a/programs/test/selftest.c b/programs/test/selftest.c
index 598c66e..229f0d8 100644
--- a/programs/test/selftest.c
+++ b/programs/test/selftest.c
@@ -353,6 +353,9 @@
     unsigned char buf[1000000];
 #endif
     void *pointer;
+#if defined(_WIN32)
+    int ci = 0; /* ci = 1 => running in CI, so don't wait for a key press */
+#endif
 
     /*
      * The C standard doesn't guarantee that all-bits-0 is the representation
@@ -380,6 +383,10 @@
         } else if (strcmp(*argp, "--exclude") == 0 ||
                    strcmp(*argp, "-x") == 0) {
             exclude_mode = 1;
+#if defined(_WIN32)
+        } else if (strcmp(*argp, "--ci") == 0) {
+            ci = 1;
+#endif
         } else {
             break;
         }
@@ -450,8 +457,10 @@
             mbedtls_printf("  [ All tests PASS ]\n\n");
         }
 #if defined(_WIN32)
-        mbedtls_printf("  Press Enter to exit this program.\n");
-        fflush(stdout); getchar();
+        if (!ci) {
+            mbedtls_printf("  Press Enter to exit this program.\n");
+            fflush(stdout); getchar();
+        }
 #endif
     }
 
diff --git a/tests/suites/test_suite_aes.ecb.data b/tests/suites/test_suite_aes.ecb.data
index 6349034..faf69c0 100644
--- a/tests/suites/test_suite_aes.ecb.data
+++ b/tests/suites/test_suite_aes.ecb.data
@@ -228,3 +228,12 @@
 
 AES-256-ECB Decrypt NIST KAT #12
 aes_decrypt_ecb:"0000000000000000000000000000000000000000000000000000000000000000":"9b80eefb7ebe2d2b16247aa0efc72f5d":"e0000000000000000000000000000000":0
+
+AES-128-ECB context alignment
+aes_ecb_context_alignment:"000102030405060708090a0b0c0d0e0f"
+
+AES-192-ECB context alignment
+aes_ecb_context_alignment:"000102030405060708090a0b0c0d0e0f1011121314151617"
+
+AES-256-ECB context alignment
+aes_ecb_context_alignment:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
diff --git a/tests/suites/test_suite_aes.function b/tests/suites/test_suite_aes.function
index 6b92b87..e96e407 100644
--- a/tests/suites/test_suite_aes.function
+++ b/tests/suites/test_suite_aes.function
@@ -1,5 +1,52 @@
 /* BEGIN_HEADER */
 #include "mbedtls/aes.h"
+
+/* Test AES with a copied context.
+ *
+ * enc and dec must be AES context objects. They don't need to
+ * be initialized, and are left freed.
+ */
+static int test_ctx_alignment(const data_t *key,
+                              mbedtls_aes_context *enc,
+                              mbedtls_aes_context *dec)
+{
+    unsigned char plaintext[16] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    };
+    unsigned char ciphertext[16];
+    unsigned char output[16];
+
+    // Set key and encrypt with original context
+    mbedtls_aes_init(enc);
+    TEST_ASSERT(mbedtls_aes_setkey_enc(enc, key->x, key->len * 8) == 0);
+    TEST_ASSERT(mbedtls_aes_crypt_ecb(enc, MBEDTLS_AES_ENCRYPT,
+                                      plaintext, ciphertext) == 0);
+
+    // Set key for decryption with original context
+    mbedtls_aes_init(dec);
+    TEST_ASSERT(mbedtls_aes_setkey_dec(dec, key->x, key->len * 8) == 0);
+
+    // Wipe the original context to make sure nothing from it is used
+    memset(enc, 0, sizeof(*enc));
+    mbedtls_aes_free(enc);
+
+    // Decrypt
+    TEST_ASSERT(mbedtls_aes_crypt_ecb(dec, MBEDTLS_AES_DECRYPT,
+                                      ciphertext, output) == 0);
+    ASSERT_COMPARE(plaintext, 16, output, 16);
+
+    mbedtls_aes_free(dec);
+
+    return 1;
+
+exit:
+    /* Bug: we may be leaving something unfreed. This is harmless
+     * in our built-in implementations, but might cause a memory leak
+     * with alternative implementations. */
+    return 0;
+}
+
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -621,6 +668,77 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE */
+void aes_ecb_context_alignment(data_t *key)
+{
+    /* We test alignment multiple times, with different alignments
+     * of the context and of the plaintext/ciphertext. */
+
+    struct align0 {
+        mbedtls_aes_context ctx;
+    };
+    struct align0 *enc0 = NULL;
+    struct align0 *dec0 = NULL;
+
+    struct align1 {
+        char bump;
+        mbedtls_aes_context ctx;
+    };
+    struct align1 *enc1 = NULL;
+    struct align1 *dec1 = NULL;
+
+    /* All peak alignment */
+    ASSERT_ALLOC(enc0, 1);
+    ASSERT_ALLOC(dec0, 1);
+    if (!test_ctx_alignment(key, &enc0->ctx, &dec0->ctx)) {
+        goto exit;
+    }
+    mbedtls_free(enc0);
+    enc0 = NULL;
+    mbedtls_free(dec0);
+    dec0 = NULL;
+
+    /* Enc aligned, dec not */
+    ASSERT_ALLOC(enc0, 1);
+    ASSERT_ALLOC(dec1, 1);
+    if (!test_ctx_alignment(key, &enc0->ctx, &dec1->ctx)) {
+        goto exit;
+    }
+    mbedtls_free(enc0);
+    enc0 = NULL;
+    mbedtls_free(dec1);
+    dec1 = NULL;
+
+    /* Dec aligned, enc not */
+    ASSERT_ALLOC(enc1, 1);
+    ASSERT_ALLOC(dec0, 1);
+    if (!test_ctx_alignment(key, &enc1->ctx, &dec0->ctx)) {
+        goto exit;
+    }
+    mbedtls_free(enc1);
+    enc1 = NULL;
+    mbedtls_free(dec0);
+    dec0 = NULL;
+
+    /* Both shifted */
+    ASSERT_ALLOC(enc1, 1);
+    ASSERT_ALLOC(dec1, 1);
+    if (!test_ctx_alignment(key, &enc1->ctx, &dec1->ctx)) {
+        goto exit;
+    }
+    mbedtls_free(enc1);
+    enc1 = NULL;
+    mbedtls_free(dec1);
+    dec1 = NULL;
+
+exit:
+    mbedtls_free(enc0);
+    mbedtls_free(dec0);
+    mbedtls_free(enc1);
+    mbedtls_free(dec1);
+}
+/* END_CASE */
+
 /* BEGIN_CASE depends_on:MBEDTLS_SELF_TEST */
 void aes_selftest()
 {
diff --git a/tests/suites/test_suite_version.data b/tests/suites/test_suite_version.data
index 69f8a84..4b57192 100644
--- a/tests/suites/test_suite_version.data
+++ b/tests/suites/test_suite_version.data
@@ -1,8 +1,8 @@
 Check compile time library version
-check_compiletime_version:"2.28.2"
+check_compiletime_version:"2.28.3"
 
 Check runtime library version
-check_runtime_version:"2.28.2"
+check_runtime_version:"2.28.3"
 
 Check for MBEDTLS_VERSION_C
 check_feature:"MBEDTLS_VERSION_C":0